2015-08-25 17:10:12 +02:00
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* drivers/mtd/s25fl1.c
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* Driver for QuadSPI-based S25FL116K, S25FL132K, and S25L164K
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
|
|
|
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
|
* are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
|
* distribution.
|
|
|
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
|
|
|
* used to endorse or promote products derived from this software
|
|
|
|
|
* without specific prior written permission.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
|
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Included Files
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
|
#include <nuttx/fs/ioctl.h>
|
2015-08-26 01:20:54 +02:00
|
|
|
|
#include <nuttx/spi/qspi.h>
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#include <nuttx/mtd/mtd.h>
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Pre-processor Definitions
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
/* Configuration ********************************************************************/
|
2015-08-30 02:54:20 +02:00
|
|
|
|
/* QuadSPI Mode. Per data sheet, either Mode 0 or Mode 3 may be used. */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifndef CONFIG_S25FL1_QSPIMODE
|
|
|
|
|
# define CONFIG_S25FL1_QSPIMODE QSPIDEV_MODE0
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-08-30 02:54:20 +02:00
|
|
|
|
/* QuadSPI Frequency per data sheet::
|
|
|
|
|
*
|
|
|
|
|
* – Normal Read (Serial):
|
|
|
|
|
* 50 MHz clock rate (-40°C to +85°C/105°C)
|
|
|
|
|
* 45 MHz clock rate (-40°C to +125°C)
|
|
|
|
|
* – Fast Read (Serial):
|
|
|
|
|
* 108 MHz clock rate (-40°C to +85°C/105°C)
|
|
|
|
|
* 97 MHz clock rate (-40°C to +125°C)
|
|
|
|
|
* – Dual Read:
|
|
|
|
|
* 108 MHz clock rate (-40°C to +85°C/105°C)
|
|
|
|
|
* 97 MHz clock rate (-40°C to +125°C)
|
|
|
|
|
* – Quad Read:
|
|
|
|
|
* 108 MHz clock rate (-40°C to +85°C/105°C)
|
|
|
|
|
* 97 MHz clock rate for S25FL164K (-40°C to +125°C)
|
|
|
|
|
*
|
|
|
|
|
* Table 5.8:
|
|
|
|
|
* - Clock frequency for all SPI commands except for Read Data
|
|
|
|
|
* command (0x03) and Fast Read command (0x0b): 108 MHz
|
|
|
|
|
* - Clock frequency for Read Data command (0x03): 50 MHz
|
|
|
|
|
* - Clock frequency for all Fast Read commands SIO and MIO: 108 MHz
|
|
|
|
|
*
|
|
|
|
|
* In this implementation, only "Quad" reads are performed.
|
|
|
|
|
*/
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifndef CONFIG_S25FL1_QSPI_FREQUENCY
|
|
|
|
|
# define CONFIG_S25FL1_QSPI_FREQUENCY 108000000
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
/* S25FL1 Commands ******************************************************************/
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* Configuration, Status, Erase, Program Commands ***********************************/
|
|
|
|
|
/* Command Value Description: */
|
|
|
|
|
/* Data sequence */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_READ_STATUS1 0x05 /* Read status register 1: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x05 | SR1 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_READ_STATUS2 0x35 /* Read status register 2: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x35 | SR2 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_READ_STATUS3 0x33 /* Read status register 3: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x33 | SR3 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_WRITE_ENABLE 0x06 /* Write enable: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x06 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_VWRITE_ENABLE 0x50 /* Write enable for volatile status: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x50 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_WRITE_DISABLE 0x04 /* Write disable command code: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x04 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_WRITE_STATUS 0x01 /* Write status register: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x01 | SR1 | SR2 | SR3 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_WRAP_ENABLE 0x77 /* Set Burst with Wrap: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x77 | xx | xx | xx | SR3 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_UNPROTECT_SECTOR 0x39 /* Set Block / Pointer Protection: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x39 | ADDR(MS) | ADDR(MID) | xx */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_PAGE_PROGRAM 0x02 /* Page Program: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x02 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | data */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_SECTOR_ERASE 0x20 /* Sector Erase (4 kB) *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x02 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_BLOCK_ERASE 0xd8 /* Block Erase (64 kB): *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x02 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_CHIP_ERASE_1 0x60 /* Chip Erase 1: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x60 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_CHIP_ERASE_2 0xc7 /* Chip Erase 2: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0xc7 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_ERASE_PROG_SUSPEND 0x75 /* Erase / Program Suspend: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x75 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_ERASE_PROG_RESUME 0x7a /* Erase / Program Resume: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x7a */
|
|
|
|
|
|
|
|
|
|
/* Read Commands ********************************************************************/
|
|
|
|
|
/* Command Value Description: */
|
|
|
|
|
/* Data sequence */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_READ_DATA 0x03 /* Read Data: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x03 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_FAST_READ 0x0b /* Fast Read: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x0b | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | dummy | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_FAST_READ_DUAL 0x3b /* Fast Read Dual Output: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x3b | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | dummy | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_FAST_READ_QUAD 0x6b /* Fast Read Dual Output: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x6b | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | dummy | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_FAST_READ_DUALIO 0xbb /* Fast Read Dual I/O: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0xbb | ADDR(MS) | ADDR(LS) | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_FAST_READ_QUADIO 0xeb /* Fast Read Quad I/O: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0xeb | ADDR | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_CONT_READ_RESET 0xff /* Continuous Read Mode Reset: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0xff | 0xff */
|
|
|
|
|
|
|
|
|
|
/* Reset Commands *******************************************************************/
|
|
|
|
|
/* Command Value Description: */
|
|
|
|
|
/* Data sequence */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_SOFT_RESET_ENABLE 0x66 /* Software Reset Enable: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x66 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_SOFT_RESET 0x99 /* Software Reset: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x99 */
|
|
|
|
|
/* Continuous Read Mode Reset: *
|
|
|
|
|
* 0xff | 0xff */
|
|
|
|
|
|
|
|
|
|
/* ID/Security Commands *************************&***********************************/
|
|
|
|
|
/* Command Value Description: */
|
|
|
|
|
/* Data sequence */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_DEEP_PWRDOWN 0xb9 /* Deep Power-down: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0xb9 */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_RELEASE_PWRDOWN 0xab /* Release Power down / Device ID: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0xab | dummy | dummy | dummy | *
|
|
|
|
|
* DeviceID */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_MANUFACTURER 0x90 /* Manufacturer / Device ID: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x90 | dummy | dummy | 0x00 | *
|
|
|
|
|
* Manufacturer | DeviceID */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_JEDEC_ID 0x9f /* JEDEC ID: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x9f | Manufacturer | MemoryType | *
|
|
|
|
|
* Capacity */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_READ_SFDP 0x5a /* Read SFDP Register / Read Unique ID *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* Number: *
|
|
|
|
|
* 0x5a | 0x00 | 0x00 | ADDR | dummy | *
|
|
|
|
|
* data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_READ_SECURITY 0x48 /* Read Security Registers: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x48 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | dummy | data... */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_ERASE_SECURITY 0x44 /* Erase Security Registers: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x48 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_PROgRAM_SECURITY 0x42 /* Program Security Registers: *
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* 0x42 | ADDR(MS) | ADDR(MID) | *
|
|
|
|
|
* ADDR(LS) | data... */
|
|
|
|
|
|
|
|
|
|
/* Flash Manufacturer JEDEC IDs */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_JEDEC_ID_SPANSION 0x01
|
|
|
|
|
#define S25FL1_JEDEC_ID_ATMEL 0x1f
|
|
|
|
|
#define S25FL1_JEDEC_ID_ST 0x20
|
|
|
|
|
#define S25FL1_JEDEC_ID_SST 0xbf
|
|
|
|
|
#define S25FL1_JEDEC_ID_MACRONIX 0xc2
|
|
|
|
|
#define S25FL1_JEDEC_ID_WINBOND 0xef
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
/* S25FL1 JEDIC IDs */
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_JEDEC_DEVICE_TYPE 0x40 /* S25FL1 memory devuce type */
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#define S25FL116K_JEDEC_CAPACITY 0x15 /* S25FL116K memory capacity */
|
|
|
|
|
#define S25FL132K_JEDEC_CAPACITY 0x16 /* S25FL132K memory capacity */
|
|
|
|
|
#define S25FL164K_JEDEC_CAPACITY 0x17 /* S25FL164K memory capacity */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
/* S25FL1 Registers ****************************************************************/
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* Status register bit definitions */
|
|
|
|
|
|
|
|
|
|
#define STATUS1_BUSY_MASK (1 << 0) /* Bit 0: Device ready/busy status */
|
|
|
|
|
# define STATUS1_READY (0 << 0) /* 0 = Not Busy */
|
|
|
|
|
# define STATUS1_BUSY (1 << 0) /* 1 = Busy */
|
|
|
|
|
#define STATUS1_WEL_MASK (1 << 1) /* Bit 1: Write enable latch status */
|
|
|
|
|
# define STATUS1_WEL_DISABLED (0 << 1) /* 0 = Not Write Enabled */
|
|
|
|
|
# define STATUS1_WEL_ENABLED (1 << 1) /* 1 = Write Enabled */
|
|
|
|
|
#define STATUS1_BP_SHIFT (2) /* Bits 2-4: Block protect bits */
|
|
|
|
|
#define STATUS1_BP_MASK (7 << STATUS1_BP_SHIFT)
|
|
|
|
|
# define STATUS1_BP_NONE (0 << STATUS1_BP_SHIFT)
|
|
|
|
|
# define STATUS1_BP_ALL (7 << STATUS1_BP_SHIFT)
|
|
|
|
|
#define STATUS1_TB_MASK (1 << 5) /* Bit 5: Top / Bottom Protect */
|
|
|
|
|
# define STATUS1_TB_TOP (0 << 5) /* 0 = BP2-BP0 protect Top down */
|
|
|
|
|
# define STATUS1_TB_BOTTOM (1 << 5) /* 1 = BP2-BP0 protect Bottom up */
|
|
|
|
|
#define STATUS1_SEC_MASK (1 << 6) /* Bit 6: Sector / Block Protect */
|
|
|
|
|
# define STATUS1_SEC_BLOCK (0 << 6) /* 0 = BP2-BP0 protect 64-kB blocks */
|
2015-08-26 22:16:45 +02:00
|
|
|
|
# define STATUS1_SEC_SECTOR (1 << 6) /* 1 = BP2-BP0 protect 4-kB sectors */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#define STATUS1_SRP0_MASK (1 << 7) /* Bit 7: Status register protect 0 */
|
|
|
|
|
# define STATUS1_SRP0_UNLOCKED (0 << 7) /* 0 = WP# no effect / PS Lock Down */
|
|
|
|
|
# define STATUS1_SRP0_LOCKED (1 << 7) /* 1 = WP# protect / OTP Lock Down */
|
|
|
|
|
|
|
|
|
|
#define STATUS2_SRP1_MASK (1 << 0) /* Bit 0: Status register protect 1 */
|
|
|
|
|
# define STATUS2_SRP1_UNLOCKED (0 << 0) /* 0 = WP# no effect / PS Lock Down */
|
|
|
|
|
# define STATUS2_SRP1_LOCKED (1 << 0) /* 1 = WP# protect / OTP Lock Down */
|
|
|
|
|
#define STATUS2_QUAD_ENABLE_MASK (1 << 1) /* Bit 1: Quad Enable */
|
|
|
|
|
# define STATUS2_QUAD_DISABLE (0 << 1) /* 0 = Quad Mode Not Enabled */
|
|
|
|
|
# define STATUS2_QUAD_ENABLE (1 << 1) /* 1 = Quad Mode Enabled */
|
|
|
|
|
#define STATUS2_LB_SHIFT (2) /* Bits 2-5: Security Register Lock */
|
|
|
|
|
#define STATUS2_LB_MASK (15 << STATUS2_LB_SHIFT)
|
|
|
|
|
# define STATUS2_LB_NONE (0 << STATUS2_LB_SHIFT)
|
|
|
|
|
# define STATUS2_LB_ALL (15 << STATUS2_LB_SHIFT)
|
|
|
|
|
#define STATUS2_CMP_MASK (1 << 6) /* Bit 6: Complement Protect */
|
|
|
|
|
# define STATUS2_CMP_NORMAL (0 << 6) /* 0 = Normal Protection Map */
|
|
|
|
|
# define STATUS2_CMP_INVERTED (1 << 6) /* 1 = Inverted Protection Map */
|
|
|
|
|
#define STATUS2_SUS_MASK (1 << 7) /* Bit 7: Suspend Status */
|
|
|
|
|
# define STATUS2_SUS_NONE (0 << 7) /* 0 = Erase / Program not suspended */
|
|
|
|
|
# define STATUS2_SUS_SUSPENDED (1 << 7) /* 1 = Erase / Program suspended */
|
|
|
|
|
|
|
|
|
|
#define STATUS3_LC_SHIFT (0) /* Bits 0-3: Latency control */
|
|
|
|
|
#define STATUS3_LC_MASK (15 << STATUS3_LC_SHIFT)
|
|
|
|
|
#define STATUS3_W4_MASK (1 << 4) /* Bit 4: Burst Wrap Enable */
|
|
|
|
|
# define STATUS3_W4_DISABLED (0 << 4) /* 0 = Wrap Enabled */
|
|
|
|
|
# define STATUS3_W4_ENABLED (1 << 4) /* 1 = Wrap Disabled */
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define STATUS3_W56_SHIFT (5) /* Bits 5-6: Burst Wrap Length */
|
|
|
|
|
#define STATUS3_W56_MASK (3 << STATUS3_W56_SHIFT)
|
|
|
|
|
# define STATUS3_W56_8BYTE (0 << STATUS3_W56_SHIFT)
|
|
|
|
|
# define STATUS3_W56_16BYTE (1 << STATUS3_W56_SHIFT)
|
|
|
|
|
# define STATUS3_W56_32BYTE (2 << STATUS3_W56_SHIFT)
|
|
|
|
|
# define STATUS3_W56_63BYTE (3 << STATUS3_W56_SHIFT)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* Bit 7: Reserved */
|
|
|
|
|
|
|
|
|
|
/* Chip Geometries ******************************************************************/
|
|
|
|
|
/* All members of the family support uniform 4K-byte sectors */
|
|
|
|
|
|
|
|
|
|
#define S25FL116K_SECTOR_SIZE (4*1024)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#define S25FL116K_SECTOR_SHIFT (12)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#define S25FL116K_SECTOR_COUNT (512)
|
2015-08-28 18:15:39 +02:00
|
|
|
|
#define S25FL116K_PAGE_SIZE (256)
|
|
|
|
|
#define S25FL116K_PAGE_SHIFT (8)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
#define S25FL132K_SECTOR_SIZE (4*1024)
|
2015-08-28 18:15:39 +02:00
|
|
|
|
#define S25FL132K_SECTOR_SHIFT (12)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#define S25FL132K_SECTOR_COUNT (1024)
|
2015-08-28 18:15:39 +02:00
|
|
|
|
#define S25FL132K_PAGE_SIZE (256)
|
|
|
|
|
#define S25FL132K_PAGE_SHIFT (8)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
#define S25FL164K_SECTOR_SIZE (4*1024)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#define S25FL164K_SECTOR_SHIFT (12)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#define S25FL164K_SECTOR_COUNT (2048)
|
2015-08-28 18:15:39 +02:00
|
|
|
|
#define S25FL164K_PAGE_SIZE (256)
|
|
|
|
|
#define S25FL164K_PAGE_SHIFT (8)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Cache flags **********************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define S25FL1_CACHE_VALID (1 << 0) /* 1=Cache has valid data */
|
|
|
|
|
#define S25FL1_CACHE_DIRTY (1 << 1) /* 1=Cache is dirty */
|
|
|
|
|
#define S25FL1_CACHE_ERASED (1 << 2) /* 1=Backing FLASH is erased */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define IS_VALID(p) ((((p)->flags) & S25FL1_CACHE_VALID) != 0)
|
|
|
|
|
#define IS_DIRTY(p) ((((p)->flags) & S25FL1_CACHE_DIRTY) != 0)
|
|
|
|
|
#define IS_ERASED(p) ((((p)->flags) & S25FL1_CACHE_DIRTY) != 0)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define SET_VALID(p) do { (p)->flags |= S25FL1_CACHE_VALID; } while (0)
|
|
|
|
|
#define SET_DIRTY(p) do { (p)->flags |= S25FL1_CACHE_DIRTY; } while (0)
|
|
|
|
|
#define SET_ERASED(p) do { (p)->flags |= S25FL1_CACHE_DIRTY; } while (0)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#define CLR_VALID(p) do { (p)->flags &= ~S25FL1_CACHE_VALID; } while (0)
|
|
|
|
|
#define CLR_DIRTY(p) do { (p)->flags &= ~S25FL1_CACHE_DIRTY; } while (0)
|
|
|
|
|
#define CLR_ERASED(p) do { (p)->flags &= ~S25FL1_CACHE_DIRTY; } while (0)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-10 20:34:33 +01:00
|
|
|
|
/* 512 byte sector support **********************************************************/
|
|
|
|
|
|
|
|
|
|
#define S25FL1_SECTOR512_SHIFT 9
|
|
|
|
|
#define S25FL1_SECTOR512_SIZE (1 << 9)
|
|
|
|
|
#define S25FL1_ERASED_STATE 0xff
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Private Types
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/* This type represents the state of the MTD device. The struct mtd_dev_s must
|
|
|
|
|
* appear at the beginning of the definition so that you can freely cast between
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* pointers to struct mtd_dev_s and struct s25fl1_dev_s.
|
2015-08-25 17:10:12 +02:00
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
struct s25fl1_dev_s
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-09-06 17:37:34 +02:00
|
|
|
|
struct mtd_dev_s mtd; /* MTD interface */
|
|
|
|
|
FAR struct qspi_dev_s *qspi; /* Saved QuadSPI interface instance */
|
|
|
|
|
uint16_t nsectors; /* Number of erase sectors */
|
|
|
|
|
uint8_t sectorshift; /* Log2 of sector size */
|
|
|
|
|
uint8_t pageshift; /* Log2 of page size */
|
|
|
|
|
FAR uint8_t *cmdbuf; /* Allocated command buffer */
|
2015-11-05 17:32:56 +01:00
|
|
|
|
FAR uint8_t *readbuf; /* Allocated status read buffer */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
2015-09-06 17:37:34 +02:00
|
|
|
|
uint8_t flags; /* Buffered sector flags */
|
2015-10-10 18:41:00 +02:00
|
|
|
|
uint16_t esectno; /* Erase sector number in the cache */
|
2015-09-06 17:37:34 +02:00
|
|
|
|
FAR uint8_t *sector; /* Allocated sector data */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Private Function Prototypes
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Locking */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static void s25fl1_lock(FAR struct qspi_dev_s *qspi);
|
|
|
|
|
static inline void s25fl1_unlock(FAR struct qspi_dev_s *qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Low-level message helpers */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command(FAR struct qspi_dev_s *qspi, uint8_t cmd);
|
|
|
|
|
static int s25fl1_command_address(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
2015-08-27 22:16:51 +02:00
|
|
|
|
off_t addr, uint8_t addrlen);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command_read(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
FAR void *buffer, size_t buflen);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command_write(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
FAR const void *buffer, size_t buflen);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static uint8_t sf25fl1_read_status1(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
static uint8_t sf25fl1_read_status2(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
static uint8_t sf25fl1_read_status3(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
static void s25fl1_write_enable(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
static void s25fl1_write_disable(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
|
|
|
|
|
static int s25fl1_readid(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
static int s25fl1_protect(FAR struct s25fl1_dev_s *priv,
|
2015-08-29 23:58:54 +02:00
|
|
|
|
off_t startblock, size_t nblocks);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_unprotect(FAR struct s25fl1_dev_s *priv,
|
2015-08-29 23:58:54 +02:00
|
|
|
|
off_t startblock, size_t nblocks);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static bool s25fl1_isprotected(FAR struct s25fl1_dev_s *priv,
|
2015-08-29 23:58:54 +02:00
|
|
|
|
uint8_t status, off_t address);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_erase_sector(FAR struct s25fl1_dev_s *priv, off_t offset);
|
|
|
|
|
static int s25fl1_erase_chip(FAR struct s25fl1_dev_s *priv);
|
|
|
|
|
static int s25fl1_read_byte(FAR struct s25fl1_dev_s *priv, FAR uint8_t *buffer,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
off_t address, size_t nbytes);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_write_page(FAR struct s25fl1_dev_s *priv,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
FAR const uint8_t *buffer, off_t address, size_t nbytes);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
static int s25fl1_flush_cache(struct s25fl1_dev_s *priv);
|
|
|
|
|
static FAR uint8_t *s25fl1_read_cache(struct s25fl1_dev_s *priv, off_t sector);
|
|
|
|
|
static void s25fl1_erase_cache(struct s25fl1_dev_s *priv, off_t sector);
|
|
|
|
|
static int s25fl1_write_cache(FAR struct s25fl1_dev_s *priv,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
FAR const uint8_t *buffer, off_t sector, size_t nsectors);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* MTD driver methods */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_erase(FAR struct mtd_dev_s *dev, off_t startblock,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
size_t nblocks);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static ssize_t s25fl1_bread(FAR struct mtd_dev_s *dev, off_t startblock,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
size_t nblocks, FAR uint8_t *buf);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static ssize_t s25fl1_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
size_t nblocks, FAR const uint8_t *buf);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static ssize_t s25fl1_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
|
2015-08-26 22:16:45 +02:00
|
|
|
|
FAR uint8_t *buffer);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Private Data
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Private Functions
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_lock
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static void s25fl1_lock(FAR struct qspi_dev_s *qspi)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
|
|
|
|
/* On QuadSPI busses where there are multiple devices, it will be necessary to
|
|
|
|
|
* lock QuadSPI to have exclusive access to the busses for a sequence of
|
|
|
|
|
* transfers. The bus should be locked before the chip is selected.
|
|
|
|
|
*
|
|
|
|
|
* This is a blocking call and will not return until we have exclusiv access to
|
|
|
|
|
* the QuadSPI buss. We will retain that exclusive access until the bus is unlocked.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-08-26 01:20:54 +02:00
|
|
|
|
(void)QSPI_LOCK(qspi, true);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* After locking the QuadSPI bus, the we also need call the setfrequency, setbits, and
|
|
|
|
|
* setmode methods to make sure that the QuadSPI is properly configured for the device.
|
|
|
|
|
* If the QuadSPI buss is being shared, then it may have been left in an incompatible
|
|
|
|
|
* state.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
QSPI_SETMODE(qspi, CONFIG_S25FL1_QSPIMODE);
|
2015-08-26 01:20:54 +02:00
|
|
|
|
QSPI_SETBITS(qspi, 8);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
(void)QSPI_SETFREQUENCY(qspi, CONFIG_S25FL1_QSPI_FREQUENCY);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_unlock
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static inline void s25fl1_unlock(FAR struct qspi_dev_s *qspi)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-26 01:20:54 +02:00
|
|
|
|
(void)QSPI_LOCK(qspi, false);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_command
|
2015-08-26 22:16:45 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command(FAR struct qspi_dev_s *qspi, uint8_t cmd)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-08-28 18:15:39 +02:00
|
|
|
|
struct qspi_cmdinfo_s cmdinfo;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("CMD: %02x\n", cmd);
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
cmdinfo.flags = 0;
|
|
|
|
|
cmdinfo.addrlen = 0;
|
|
|
|
|
cmdinfo.cmd = cmd;
|
|
|
|
|
cmdinfo.buflen = 0;
|
|
|
|
|
cmdinfo.addr = 0;
|
|
|
|
|
cmdinfo.buffer = NULL;
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
return QSPI_COMMAND(qspi, &cmdinfo);
|
2015-08-27 22:16:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_command_address
|
2015-08-27 22:16:51 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command_address(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
|
|
|
off_t addr, uint8_t addrlen)
|
2015-08-27 22:16:51 +02:00
|
|
|
|
{
|
2015-08-28 18:15:39 +02:00
|
|
|
|
struct qspi_cmdinfo_s cmdinfo;
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("CMD: %02x Address: %04lx addrlen=%d\n", cmd, (unsigned long)addr, addrlen);
|
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
cmdinfo.flags = QSPICMD_ADDRESS;
|
|
|
|
|
cmdinfo.addrlen = addrlen;
|
|
|
|
|
cmdinfo.cmd = cmd;
|
|
|
|
|
cmdinfo.buflen = 0;
|
|
|
|
|
cmdinfo.addr = addr;
|
|
|
|
|
cmdinfo.buffer = NULL;
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
return QSPI_COMMAND(qspi, &cmdinfo);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_command_read
|
2015-08-26 22:16:45 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command_read(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
|
|
|
FAR void *buffer, size_t buflen)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-08-28 18:15:39 +02:00
|
|
|
|
struct qspi_cmdinfo_s cmdinfo;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
cmdinfo.flags = QSPICMD_READDATA;
|
|
|
|
|
cmdinfo.addrlen = 0;
|
|
|
|
|
cmdinfo.cmd = cmd;
|
|
|
|
|
cmdinfo.buflen = buflen;
|
|
|
|
|
cmdinfo.addr = 0;
|
|
|
|
|
cmdinfo.buffer = buffer;
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
return QSPI_COMMAND(qspi, &cmdinfo);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_command_write
|
2015-08-26 22:16:45 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_command_write(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
|
|
|
FAR const void *buffer, size_t buflen)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-08-28 18:15:39 +02:00
|
|
|
|
struct qspi_cmdinfo_s cmdinfo;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
cmdinfo.flags = QSPICMD_WRITEDATA;
|
|
|
|
|
cmdinfo.addrlen = 0;
|
|
|
|
|
cmdinfo.cmd = cmd;
|
|
|
|
|
cmdinfo.buflen = buflen;
|
|
|
|
|
cmdinfo.addr = 0;
|
|
|
|
|
cmdinfo.buffer = (FAR void *)buffer;
|
2015-08-27 22:16:51 +02:00
|
|
|
|
|
2015-08-28 18:15:39 +02:00
|
|
|
|
return QSPI_COMMAND(qspi, &cmdinfo);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Name: sf25fl1_read_status1
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static uint8_t sf25fl1_read_status1(FAR struct s25fl1_dev_s *priv)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
DEBUGVERIFY(s25fl1_command_read(priv->qspi, S25FL1_READ_STATUS1,
|
|
|
|
|
(FAR void *)&priv->readbuf[0], 1));
|
2015-11-05 17:32:56 +01:00
|
|
|
|
return priv->readbuf[0];
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Name: sf25fl1_read_status2
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static uint8_t sf25fl1_read_status2(FAR struct s25fl1_dev_s *priv)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
DEBUGVERIFY(s25fl1_command_read(priv->qspi, S25FL1_READ_STATUS2,
|
|
|
|
|
(FAR void *)&priv->readbuf[0], 1));
|
2015-11-05 17:32:56 +01:00
|
|
|
|
return priv->readbuf[0];
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Name: sf25fl1_read_status3
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static uint8_t sf25fl1_read_status3(FAR struct s25fl1_dev_s *priv)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
DEBUGVERIFY(s25fl1_command_read(priv->qspi, S25FL1_READ_STATUS3,
|
|
|
|
|
(FAR void *)&priv->readbuf[0], 1));
|
2015-11-05 17:32:56 +01:00
|
|
|
|
return priv->readbuf[0];
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_write_enable
|
2015-08-26 22:16:45 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static void s25fl1_write_enable(FAR struct s25fl1_dev_s *priv)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_command(priv->qspi, S25FL1_WRITE_ENABLE);
|
2015-09-06 17:37:34 +02:00
|
|
|
|
status = sf25fl1_read_status1(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
while ((status & STATUS1_WEL_MASK) != STATUS1_WEL_ENABLED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_write_disable
|
2015-08-26 22:16:45 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static void s25fl1_write_disable(FAR struct s25fl1_dev_s *priv)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_command(priv->qspi, S25FL1_WRITE_DISABLE);
|
2015-09-06 17:37:34 +02:00
|
|
|
|
status = sf25fl1_read_status1(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
while ((status & STATUS1_WEL_MASK) != STATUS1_WEL_DISABLED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_write_status
|
2015-08-26 22:16:45 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static void s25fl1_write_status(FAR struct s25fl1_dev_s *priv)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_enable(priv);
|
|
|
|
|
s25fl1_command_write(priv->qspi, S25FL1_WRITE_STATUS,
|
|
|
|
|
(FAR const void *)priv->cmdbuf, 3);
|
|
|
|
|
s25fl1_write_disable(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_readid
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static inline int s25fl1_readid(struct s25fl1_dev_s *priv)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-26 01:20:54 +02:00
|
|
|
|
/* Lock the QuadSPI bus and configure the bus. */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_lock(priv->qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Read the JEDEC ID */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_command_read(priv->qspi, S25FL1_JEDEC_ID, priv->cmdbuf, 3);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 01:20:54 +02:00
|
|
|
|
/* Unlock the bus */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_unlock(priv->qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
fvdbg("Manufacturer: %02x Device Type %02x, Capacity: %02x",
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0], priv->cmdbuf[1], priv->cmdbuf[2]);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Check for a recognized memory device type */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
if (priv->cmdbuf[1] != S25FL1_JEDEC_DEVICE_TYPE)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
2015-09-06 17:37:34 +02:00
|
|
|
|
fdbg("ERROR: Unrecognized device type: %02x\n", priv->cmdbuf[1]);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Check for a supported capacity */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
switch (priv->cmdbuf[2])
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
|
|
|
|
case S25FL116K_JEDEC_CAPACITY:
|
|
|
|
|
priv->sectorshift = S25FL116K_SECTOR_SHIFT;
|
2015-08-28 18:15:39 +02:00
|
|
|
|
priv->pageshift = S25FL116K_PAGE_SHIFT;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
priv->nsectors = S25FL116K_SECTOR_COUNT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case S25FL132K_JEDEC_CAPACITY:
|
2015-08-28 18:15:39 +02:00
|
|
|
|
priv->sectorshift = S25FL132K_SECTOR_SHIFT;
|
|
|
|
|
priv->pageshift = S25FL116K_PAGE_SHIFT;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
priv->nsectors = S25FL132K_SECTOR_COUNT;
|
|
|
|
|
break;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
case S25FL164K_JEDEC_CAPACITY:
|
|
|
|
|
priv->sectorshift = S25FL164K_SECTOR_SHIFT;
|
2015-08-28 18:15:39 +02:00
|
|
|
|
priv->pageshift = S25FL116K_PAGE_SHIFT;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
priv->nsectors = S25FL164K_SECTOR_COUNT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Support for this part is not implemented yet */
|
|
|
|
|
|
|
|
|
|
default:
|
2015-09-06 17:37:34 +02:00
|
|
|
|
fdbg("ERROR: Unsupported memory capacity: %02x\n", priv->cmdbuf[2]);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
return -ENODEV;
|
2015-10-04 23:04:00 +02:00
|
|
|
|
}
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
2015-10-04 23:04:00 +02:00
|
|
|
|
return OK;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-29 23:58:54 +02:00
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_protect
|
2015-08-29 23:58:54 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_protect(FAR struct s25fl1_dev_s *priv,
|
|
|
|
|
off_t startblock, size_t nblocks)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
/* Get the status register value to check the current protection */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] = sf25fl1_read_status1(priv);
|
|
|
|
|
priv->cmdbuf[1] = sf25fl1_read_status2(priv);
|
|
|
|
|
priv->cmdbuf[2] = sf25fl1_read_status3(priv);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
if ((priv->cmdbuf[0] & STATUS1_BP_MASK) == STATUS1_BP_NONE)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
/* Protection already disabled */
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if sector protection registers are locked */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
if ((priv->cmdbuf[0] & STATUS1_SRP0_MASK) == STATUS1_SRP0_LOCKED)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
/* Yes.. unprotect section protection registers */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] &= ~STATUS1_SRP0_MASK;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_status(priv);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the protection mask to zero.
|
|
|
|
|
* REVISIT: This logic should really just set the BP bits as
|
|
|
|
|
* necessary to protect the range of sectors.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] |= STATUS1_BP_MASK;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_status(priv);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
|
|
|
|
|
/* Check the new status */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] = sf25fl1_read_status1(priv);
|
|
|
|
|
if ((priv->cmdbuf[0] & STATUS1_BP_MASK) != STATUS1_BP_MASK)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_unprotect
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_unprotect(FAR struct s25fl1_dev_s *priv,
|
|
|
|
|
off_t startblock, size_t nblocks)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
/* Get the status register value to check the current protection */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] = sf25fl1_read_status1(priv);
|
|
|
|
|
priv->cmdbuf[1] = sf25fl1_read_status2(priv);
|
|
|
|
|
priv->cmdbuf[2] = sf25fl1_read_status3(priv);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
|
2015-11-10 18:33:35 +01:00
|
|
|
|
if ((priv->cmdbuf[0] & STATUS1_BP_MASK) == STATUS1_BP_NONE &&
|
|
|
|
|
(priv->cmdbuf[1] & STATUS2_CMP_MASK) == 0)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
/* Protection already disabled */
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if sector protection registers are locked */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
if ((priv->cmdbuf[0] & STATUS1_SRP0_MASK) == STATUS1_SRP0_LOCKED)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
/* Yes.. unprotect section protection registers */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] &= ~STATUS1_SRP0_MASK;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_status(priv);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-10 18:33:35 +01:00
|
|
|
|
/* Set the protection mask to zero (and not complemented).
|
2015-08-29 23:58:54 +02:00
|
|
|
|
* REVISIT: This logic should really just re-write the BP bits as
|
|
|
|
|
* necessary to unprotect the range of sectors.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] &= ~STATUS1_BP_MASK;
|
2015-11-10 18:33:35 +01:00
|
|
|
|
priv->cmdbuf[1] &= ~STATUS2_CMP_MASK;
|
|
|
|
|
s25fl1_write_status(priv);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
|
|
|
|
|
/* Check the new status */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] = sf25fl1_read_status1(priv);
|
|
|
|
|
if ((priv->cmdbuf[0] & (STATUS1_SRP0_MASK | STATUS1_BP_MASK)) != 0)
|
2015-08-29 23:58:54 +02:00
|
|
|
|
{
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_isprotected
|
2015-08-29 23:58:54 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static bool s25fl1_isprotected(FAR struct s25fl1_dev_s *priv, uint8_t status,
|
|
|
|
|
off_t address)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-29 23:58:54 +02:00
|
|
|
|
off_t protstart;
|
|
|
|
|
off_t protend;
|
|
|
|
|
off_t protsize;
|
|
|
|
|
unsigned int bp;
|
|
|
|
|
|
|
|
|
|
/* What is protected? 64 Kb blocks? Or 4Kb sectors? */
|
|
|
|
|
|
|
|
|
|
if ((status & STATUS1_SEC_MASK) == STATUS1_SEC_BLOCK)
|
|
|
|
|
{
|
|
|
|
|
/* 64 Kb block */
|
|
|
|
|
|
|
|
|
|
protsize = 0x00010000;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* 4 Kb sector */
|
|
|
|
|
|
|
|
|
|
protsize = 0x00001000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The BP field is the essentially a multiplier on this protection size */
|
|
|
|
|
|
|
|
|
|
bp = (status & STATUS1_BP_MASK) >> STATUS1_BP_SHIFT;
|
|
|
|
|
switch (bp)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
case 7:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
protsize <<= (protsize << (bp - 1));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The final protection range then depends on if the protection region is
|
|
|
|
|
* configured top-down or bottom up (assuming CMP=0).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if ((status & STATUS1_TB_MASK) != 0)
|
|
|
|
|
{
|
|
|
|
|
protstart = 0x00000000;
|
|
|
|
|
protend = protstart + protsize;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
protend = 0x00200000;
|
|
|
|
|
protstart = protend - protsize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (address >= protstart && address < protend);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_erase_sector
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_erase_sector(struct s25fl1_dev_s *priv, off_t sector)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-27 22:16:51 +02:00
|
|
|
|
off_t address;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
uint8_t status;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
fvdbg("sector: %08lx\n", (unsigned long)sector);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Check that the flash is ready and unprotected */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
status = sf25fl1_read_status1(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
if ((status & STATUS1_BUSY_MASK) != STATUS1_READY)
|
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR: Flash busy: %02x", status);
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-29 23:58:54 +02:00
|
|
|
|
/* Get the address associated with the sector */
|
|
|
|
|
|
|
|
|
|
address = (off_t)sector << priv->sectorshift;
|
|
|
|
|
|
|
|
|
|
if ((status & STATUS1_BP_MASK) != 0 &&
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_isprotected(priv, status, address))
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR: Flash protected: %02x", status);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
return -EACCES;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send the sector erase command */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_enable(priv);
|
|
|
|
|
s25fl1_command_address(priv->qspi, S25FL1_SECTOR_ERASE, address, 3);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
/* Wait for erasure to finish */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
while ((sf25fl1_read_status1(priv) & STATUS1_BUSY_MASK) != 0);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
return OK;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_erase_chip
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_erase_chip(struct s25fl1_dev_s *priv)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-26 22:16:45 +02:00
|
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
|
|
/* Check if the FLASH is protected */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
status = sf25fl1_read_status1(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
if ((status & STATUS1_BP_MASK) != 0)
|
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR: FLASH is Protected: %02x", status);
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Erase the whole chip */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_enable(priv);
|
|
|
|
|
s25fl1_command(priv->qspi, S25FL1_CHIP_ERASE_2);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
/* Wait for the erasure to complete */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
status = sf25fl1_read_status1(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
while ((status & STATUS1_BUSY_MASK) != 0)
|
|
|
|
|
{
|
|
|
|
|
usleep(200*1000);
|
2015-09-06 17:37:34 +02:00
|
|
|
|
status = sf25fl1_read_status1(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OK;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_read_byte
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_read_byte(FAR struct s25fl1_dev_s *priv, FAR uint8_t *buffer,
|
|
|
|
|
off_t address, size_t buflen)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-28 20:01:08 +02:00
|
|
|
|
struct qspi_meminfo_s meminfo;
|
|
|
|
|
|
2015-08-31 17:25:14 +02:00
|
|
|
|
fvdbg("address: %08lx nbytes: %d\n", (long)address, (int)buflen);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SCRAMBLE
|
2015-08-28 20:01:08 +02:00
|
|
|
|
meminfo.flags = QSPIMEM_READ | QSPIMEM_QUADIO | QSPIMEM_SCRAMBLE;
|
|
|
|
|
#else
|
|
|
|
|
meminfo.flags = QSPIMEM_READ | QSPIMEM_QUADIO;
|
|
|
|
|
#endif
|
|
|
|
|
meminfo.addrlen = 3;
|
|
|
|
|
meminfo.dummies = 6;
|
|
|
|
|
meminfo.buflen = buflen;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
meminfo.cmd = S25FL1_FAST_READ_QUADIO;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
meminfo.addr = address;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SCRAMBLE
|
|
|
|
|
meminfo.key = CONFIG_S25FL1_SCRAMBLE_KEY;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
#endif
|
|
|
|
|
meminfo.buffer = buffer;
|
|
|
|
|
|
|
|
|
|
return QSPI_MEMORY(priv->qspi, &meminfo);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_write_page
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_write_page(struct s25fl1_dev_s *priv, FAR const uint8_t *buffer,
|
|
|
|
|
off_t address, size_t buflen)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-28 20:01:08 +02:00
|
|
|
|
struct qspi_meminfo_s meminfo;
|
|
|
|
|
unsigned int pagesize;
|
|
|
|
|
unsigned int npages;
|
|
|
|
|
int ret;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
fvdbg("address: %08lx buflen: %u\n", (unsigned long)address, (unsigned)buflen);
|
|
|
|
|
|
|
|
|
|
npages = (buflen >> priv->pageshift);
|
|
|
|
|
pagesize = (1 << priv->pageshift);
|
|
|
|
|
|
|
|
|
|
/* Set up non-varying parts of transfer description */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SCRAMBLE
|
2015-08-28 20:01:08 +02:00
|
|
|
|
meminfo.flags = QSPIMEM_WRITE | QSPIMEM_SCRAMBLE;
|
|
|
|
|
#else
|
|
|
|
|
meminfo.flags = QSPIMEM_WRITE;
|
|
|
|
|
#endif
|
2015-11-07 18:51:09 +01:00
|
|
|
|
meminfo.cmd = S25FL1_PAGE_PROGRAM;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
meminfo.addrlen = 3;
|
|
|
|
|
meminfo.buflen = pagesize;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SCRAMBLE
|
|
|
|
|
meminfo.key = CONFIG_S25FL1_SCRAMBLE_KEY;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
#endif
|
|
|
|
|
meminfo.dummies = 0;
|
|
|
|
|
|
|
|
|
|
/* Then write each page */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < npages; i++)
|
|
|
|
|
{
|
|
|
|
|
/* Set up varying parts of the transfer description */
|
|
|
|
|
|
|
|
|
|
meminfo.addr = address;
|
|
|
|
|
meminfo.buffer = (void *)buffer;
|
|
|
|
|
|
|
|
|
|
/* Write one page */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_enable(priv);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
ret = QSPI_MEMORY(priv->qspi, &meminfo);
|
2015-11-10 19:48:29 +01:00
|
|
|
|
s25fl1_write_disable(priv);
|
|
|
|
|
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR: QSPI_MEMORY failed writing address=%06x\n",
|
2015-08-31 17:25:14 +02:00
|
|
|
|
address);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update for the next time through the loop */
|
|
|
|
|
|
|
|
|
|
buffer += pagesize;
|
|
|
|
|
address += pagesize;
|
|
|
|
|
buflen -= pagesize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The transfer should always be an even number of sectors and hence also
|
|
|
|
|
* pages. There should be no remainder.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(buflen == 0);
|
|
|
|
|
return OK;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_flush_cache
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
static int s25fl1_flush_cache(struct s25fl1_dev_s *priv)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-08-29 23:58:54 +02:00
|
|
|
|
int ret = OK;
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* If the cached is dirty (meaning that it no longer matches the old FLASH contents)
|
|
|
|
|
* or was erased (with the cache containing the correct FLASH contents), then write
|
|
|
|
|
* the cached erase block to FLASH.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (IS_DIRTY(priv) || IS_ERASED(priv))
|
|
|
|
|
{
|
2015-11-10 20:34:33 +01:00
|
|
|
|
off_t address;
|
|
|
|
|
|
|
|
|
|
/* Convert the erase sector numuber into a FLASH address */
|
|
|
|
|
|
|
|
|
|
address = (off_t)priv->esectno << priv->sectorshift;
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* Write entire erase block to FLASH */
|
2015-08-29 23:58:54 +02:00
|
|
|
|
|
2015-11-10 20:34:33 +01:00
|
|
|
|
ret = s25fl1_write_page(priv, priv->sector, address, 1 << priv->sectorshift);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_write_page failed: %d\n", ret);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* The case is no long dirty and the FLASH is no longer erased */
|
|
|
|
|
|
|
|
|
|
CLR_DIRTY(priv);
|
|
|
|
|
CLR_ERASED(priv);
|
|
|
|
|
}
|
2015-08-28 20:01:08 +02:00
|
|
|
|
|
2015-08-29 23:58:54 +02:00
|
|
|
|
return ret;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_read_cache
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
static FAR uint8_t *s25fl1_read_cache(struct s25fl1_dev_s *priv, off_t sector)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
|
|
|
|
off_t esectno;
|
|
|
|
|
int shift;
|
|
|
|
|
int index;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
int ret;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Convert from the 512 byte sector to the erase sector size of the device. For
|
|
|
|
|
* exmample, if the actual erase sector size if 4Kb (1 << 12), then we first
|
|
|
|
|
* shift to the right by 3 to get the sector number in 4096 increments.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
shift = priv->sectorshift - S25FL1_SECTOR512_SHIFT;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
esectno = sector >> shift;
|
|
|
|
|
fvdbg("sector: %ld esectno: %d shift=%d\n", sector, esectno, shift);
|
|
|
|
|
|
|
|
|
|
/* Check if the requested erase block is already in the cache */
|
|
|
|
|
|
|
|
|
|
if (!IS_VALID(priv) || esectno != priv->esectno)
|
|
|
|
|
{
|
|
|
|
|
/* No.. Flush any dirty erase block currently in the cache */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_flush_cache(priv);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_flush_cache failed: %d\n", ret);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Read the erase block into the cache */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_read_byte(priv, priv->sector,
|
2015-11-10 20:34:33 +01:00
|
|
|
|
(esectno << priv->sectorshift),
|
|
|
|
|
(1 << priv->sectorshift));
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_read_byte failed: %d\n", ret);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Mark the sector as cached */
|
|
|
|
|
|
|
|
|
|
priv->esectno = esectno;
|
|
|
|
|
|
|
|
|
|
SET_VALID(priv); /* The data in the cache is valid */
|
|
|
|
|
CLR_DIRTY(priv); /* It should match the FLASH contents */
|
|
|
|
|
CLR_ERASED(priv); /* The underlying FLASH has not been erased */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the index to the 512 sector in the erase block that holds the argument */
|
|
|
|
|
|
|
|
|
|
index = sector & ((1 << shift) - 1);
|
|
|
|
|
|
|
|
|
|
/* Return the address in the cache that holds this sector */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
return &priv->sector[index << S25FL1_SECTOR512_SHIFT];
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_erase_cache
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
static void s25fl1_erase_cache(struct s25fl1_dev_s *priv, off_t sector)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
|
|
|
|
FAR uint8_t *dest;
|
|
|
|
|
|
|
|
|
|
/* First, make sure that the erase block containing the 512 byte sector is in
|
|
|
|
|
* the cache.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
dest = s25fl1_read_cache(priv, sector);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Erase the block containing this sector if it is not already erased.
|
|
|
|
|
* The erased indicated will be cleared when the data from the erase sector
|
|
|
|
|
* is read into the cache and set here when we erase the block.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!IS_ERASED(priv))
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
off_t esectno = sector >> (priv->sectorshift - S25FL1_SECTOR512_SHIFT);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
fvdbg("sector: %ld esectno: %d\n", sector, esectno);
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
DEBUGVERIFY(s25fl1_erase_sector(priv, esectno));
|
2015-08-25 17:10:12 +02:00
|
|
|
|
SET_ERASED(priv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Put the cached sector data into the erase state and mart the cache as dirty
|
|
|
|
|
* (but don't update the FLASH yet. The caller will do that at a more optimal
|
|
|
|
|
* time).
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
memset(dest, S25FL1_ERASED_STATE, S25FL1_SECTOR512_SIZE);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
SET_DIRTY(priv);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_write_cache
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
static int s25fl1_write_cache(FAR struct s25fl1_dev_s *priv,
|
|
|
|
|
FAR const uint8_t *buffer, off_t sector,
|
|
|
|
|
size_t nsectors)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
|
|
|
|
FAR uint8_t *dest;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
int ret;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
for (; nsectors > 0; nsectors--)
|
|
|
|
|
{
|
|
|
|
|
/* First, make sure that the erase block containing 512 byte sector is in
|
|
|
|
|
* memory.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
dest = s25fl1_read_cache(priv, sector);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Erase the block containing this sector if it is not already erased.
|
|
|
|
|
* The erased indicated will be cleared when the data from the erase sector
|
|
|
|
|
* is read into the cache and set here when we erase the sector.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!IS_ERASED(priv))
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
off_t esectno = sector >> (priv->sectorshift - S25FL1_SECTOR512_SHIFT);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
fvdbg("sector: %ld esectno: %d\n", sector, esectno);
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_erase_sector(priv, esectno);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_erase_sector failed: %d\n", ret);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
SET_ERASED(priv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the new sector data into cached erase block */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
memcpy(dest, buffer, S25FL1_SECTOR512_SIZE);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
SET_DIRTY(priv);
|
|
|
|
|
|
|
|
|
|
/* Set up for the next 512 byte sector */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
buffer += S25FL1_SECTOR512_SIZE;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
sector++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Flush the last erase block left in the cache */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
return s25fl1_flush_cache(priv);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_erase
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
FAR struct s25fl1_dev_s *priv = (FAR struct s25fl1_dev_s *)dev;
|
2015-08-26 22:16:45 +02:00
|
|
|
|
size_t blocksleft = nblocks;
|
2015-11-10 20:34:33 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
int ret;
|
|
|
|
|
#endif
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
|
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Lock access to the SPI bus until we complete the erase */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_lock(priv->qspi);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
while (blocksleft-- > 0)
|
|
|
|
|
{
|
|
|
|
|
/* Erase each sector */
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_erase_cache(priv, startblock);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#else
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_erase_sector(priv, startblock);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#endif
|
|
|
|
|
startblock++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
/* Flush the last erase block left in the cache */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_flush_cache(priv);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
nblocks = ret;
|
|
|
|
|
}
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_unlock(priv->qspi);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
return (int)nblocks;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_bread
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static ssize_t s25fl1_bread(FAR struct mtd_dev_s *dev, off_t startblock,
|
|
|
|
|
size_t nblocks, FAR uint8_t *buffer)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-11-10 20:34:33 +01:00
|
|
|
|
#ifndef CONFIG_S25FL1_SECTOR512
|
2015-11-07 18:51:09 +01:00
|
|
|
|
FAR struct s25fl1_dev_s *priv = (FAR struct s25fl1_dev_s *)dev;
|
2015-11-10 20:34:33 +01:00
|
|
|
|
#endif
|
2015-08-25 17:10:12 +02:00
|
|
|
|
ssize_t nbytes;
|
|
|
|
|
|
|
|
|
|
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
|
|
|
|
|
|
|
|
|
|
/* On this device, we can handle the block read just like the byte-oriented read */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
nbytes = s25fl1_read(dev, startblock << S25FL1_SECTOR512_SHIFT,
|
|
|
|
|
nblocks << S25FL1_SECTOR512_SHIFT, buffer);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
if (nbytes > 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
nbytes >>= S25FL1_SECTOR512_SHIFT;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
#else
|
2015-11-07 18:51:09 +01:00
|
|
|
|
nbytes = s25fl1_read(dev, startblock << priv->sectorshift,
|
|
|
|
|
nblocks << priv->sectorshift, buffer);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
if (nbytes > 0)
|
|
|
|
|
{
|
2015-08-26 22:16:45 +02:00
|
|
|
|
nbytes >>= priv->sectorshift;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return nbytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_bwrite
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static ssize_t s25fl1_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
|
|
|
|
size_t nblocks, FAR const uint8_t *buffer)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
FAR struct s25fl1_dev_s *priv = (FAR struct s25fl1_dev_s *)dev;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
int ret = (int)nblocks;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
|
|
|
|
|
|
|
|
|
|
/* Lock the QuadSPI bus and write all of the pages to FLASH */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_lock(priv->qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#if defined(CONFIG_S25FL1_SECTOR512)
|
|
|
|
|
ret = s25fl1_write_cache(priv, buffer, startblock, nblocks);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_write_cache failed: %d\n", ret);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#else
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_write_page(priv, buffer, startblock << priv->sectorshift,
|
|
|
|
|
nblocks << priv->sectorshift);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_write_page failed: %d\n", ret);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#endif
|
2015-08-28 20:01:08 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_unlock(priv->qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:26:53 +01:00
|
|
|
|
return ret < 0 ? ret : nblocks;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_read
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static ssize_t s25fl1_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
|
|
|
|
|
FAR uint8_t *buffer)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
FAR struct s25fl1_dev_s *priv = (FAR struct s25fl1_dev_s *)dev;
|
2015-08-28 20:01:08 +02:00
|
|
|
|
int ret;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
|
|
|
|
|
|
|
|
|
|
/* Lock the QuadSPI bus and select this FLASH part */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_lock(priv->qspi);
|
|
|
|
|
ret = s25fl1_read_byte(priv, buffer, offset, nbytes);
|
|
|
|
|
s25fl1_unlock(priv->qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-08-28 20:01:08 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
fdbg("ERROR: s25fl1_read_byte returned: %d\n", ret);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
return (ssize_t)ret;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
fvdbg("return nbytes: %d\n", (int)nbytes);
|
2015-08-28 20:01:08 +02:00
|
|
|
|
return (ssize_t)nbytes;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_ioctl
|
2015-08-25 17:10:12 +02:00
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
static int s25fl1_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
FAR struct s25fl1_dev_s *priv = (FAR struct s25fl1_dev_s *)dev;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
int ret = -EINVAL; /* Assume good command with bad parameters */
|
|
|
|
|
|
|
|
|
|
fvdbg("cmd: %d \n", cmd);
|
|
|
|
|
|
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
|
|
|
|
case MTDIOC_GEOMETRY:
|
|
|
|
|
{
|
2015-08-26 22:16:45 +02:00
|
|
|
|
FAR struct mtd_geometry_s *geo =
|
|
|
|
|
(FAR struct mtd_geometry_s *)((uintptr_t)arg);
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
if (geo)
|
|
|
|
|
{
|
|
|
|
|
/* Populate the geometry structure with information need to know
|
|
|
|
|
* the capacity and how to access the device.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: that the device is treated as though it where just an array
|
|
|
|
|
* of fixed size blocks. That is most likely not true, but the client
|
|
|
|
|
* will expect the device logic to do whatever is necessary to make it
|
|
|
|
|
* appear so.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512
|
|
|
|
|
geo->blocksize = (1 << S25FL1_SECTOR512_SHIFT);
|
|
|
|
|
geo->erasesize = (1 << S25FL1_SECTOR512_SHIFT);
|
|
|
|
|
geo->neraseblocks = priv->nsectors << (priv->sectorshift - S25FL1_SECTOR512_SHIFT);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#else
|
2015-08-26 22:16:45 +02:00
|
|
|
|
geo->blocksize = (1 << priv->sectorshift);
|
|
|
|
|
geo->erasesize = (1 << priv->sectorshift);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
geo->neraseblocks = priv->nsectors;
|
|
|
|
|
#endif
|
|
|
|
|
ret = OK;
|
|
|
|
|
|
|
|
|
|
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
|
|
|
|
|
geo->blocksize, geo->erasesize, geo->neraseblocks);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MTDIOC_BULKERASE:
|
|
|
|
|
{
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Erase the entire device */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_lock(priv->qspi);
|
|
|
|
|
ret = s25fl1_erase_chip(priv);
|
|
|
|
|
s25fl1_unlock(priv->qspi);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2015-08-29 23:58:54 +02:00
|
|
|
|
case MTDIOC_PROTECT:
|
|
|
|
|
{
|
|
|
|
|
FAR const struct mtd_protect_s *prot =
|
|
|
|
|
(FAR const struct mtd_protect_s *)((uintptr_t)arg);
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(prot);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_protect(priv, prot->startblock, prot->nblocks);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MTDIOC_UNPROTECT:
|
|
|
|
|
{
|
|
|
|
|
FAR const struct mtd_protect_s *prot =
|
|
|
|
|
(FAR const struct mtd_protect_s *)((uintptr_t)arg);
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(prot);
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_unprotect(priv, prot->startblock, prot->nblocks);
|
2015-08-29 23:58:54 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
default:
|
2015-08-29 23:58:54 +02:00
|
|
|
|
ret = -ENOTTY; /* Bad/unsupported command */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fvdbg("return %d\n", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
|
|
|
|
* Public Functions
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/************************************************************************************
|
2015-11-07 18:51:09 +01:00
|
|
|
|
* Name: s25fl1_initialize
|
2015-08-25 17:10:12 +02:00
|
|
|
|
*
|
|
|
|
|
* Description:
|
2015-08-31 17:25:14 +02:00
|
|
|
|
* Create an initialize MTD device instance for the QuadSPI-based ST24FL1
|
|
|
|
|
* FLASH part.
|
|
|
|
|
*
|
|
|
|
|
* MTD devices are not registered in the file system, but are created as instances
|
|
|
|
|
* that can be bound to other functions (such as a block or character driver front
|
|
|
|
|
* end).
|
2015-08-25 17:10:12 +02:00
|
|
|
|
*
|
|
|
|
|
************************************************************************************/
|
|
|
|
|
|
2015-11-10 18:33:35 +01:00
|
|
|
|
FAR struct mtd_dev_s *s25fl1_initialize(FAR struct qspi_dev_s *qspi, bool unprotect)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-11-07 18:51:09 +01:00
|
|
|
|
FAR struct s25fl1_dev_s *priv;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
int ret;
|
|
|
|
|
|
2015-08-26 01:20:54 +02:00
|
|
|
|
fvdbg("qspi: %p\n", qspi);
|
2015-09-06 17:37:34 +02:00
|
|
|
|
DEBUGASSERT(qspi != NULL);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
|
|
|
|
/* Allocate a state structure (we allocate the structure instead of using
|
|
|
|
|
* a fixed, static allocation so that we can handle multiple FLASH devices.
|
|
|
|
|
* The current implementation would handle only one FLASH part per QuadSPI
|
2015-08-26 01:20:54 +02:00
|
|
|
|
* device (only because of the QSPIDEV_FLASH definition) and so would have
|
2015-08-25 17:10:12 +02:00
|
|
|
|
* to be extended to handle multiple FLASH parts on the same QuadSPI bus.
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
priv = (FAR struct s25fl1_dev_s *)kmm_zalloc(sizeof(struct s25fl1_dev_s));
|
2015-08-25 17:10:12 +02:00
|
|
|
|
if (priv)
|
|
|
|
|
{
|
|
|
|
|
/* Initialize the allocated structure (unsupported methods were
|
|
|
|
|
* nullified by kmm_zalloc).
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
priv->mtd.erase = s25fl1_erase;
|
|
|
|
|
priv->mtd.bread = s25fl1_bread;
|
|
|
|
|
priv->mtd.bwrite = s25fl1_bwrite;
|
|
|
|
|
priv->mtd.read = s25fl1_read;
|
|
|
|
|
priv->mtd.ioctl = s25fl1_ioctl;
|
2015-08-26 01:20:54 +02:00
|
|
|
|
priv->qspi = qspi;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-05 17:32:56 +01:00
|
|
|
|
/* Allocate a 4-byte buffer to support DMA command data */
|
2015-09-06 17:37:34 +02:00
|
|
|
|
|
|
|
|
|
priv->cmdbuf = (FAR uint8_t *)QSPI_ALLOC(qspi, 4);
|
|
|
|
|
if (priv->cmdbuf == NULL)
|
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR Failed to allocate command buffer\n");
|
|
|
|
|
goto errout_with_priv;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 17:32:56 +01:00
|
|
|
|
/* Allocate a one-byte buffer to support DMA status read data */
|
|
|
|
|
|
|
|
|
|
priv->readbuf = (FAR uint8_t *)QSPI_ALLOC(qspi, 1);
|
|
|
|
|
if (priv->readbuf == NULL)
|
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR Failed to allocate read buffer\n");
|
|
|
|
|
goto errout_with_cmdbuf;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* Identify the FLASH chip and get its capacity */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
ret = s25fl1_readid(priv);
|
2015-08-25 17:10:12 +02:00
|
|
|
|
if (ret != OK)
|
|
|
|
|
{
|
|
|
|
|
/* Unrecognized! Discard all of that work we just did and return NULL */
|
|
|
|
|
|
2015-08-26 22:16:45 +02:00
|
|
|
|
fdbg("ERROR Unrecognized QSPI device\n");
|
2015-11-05 17:32:56 +01:00
|
|
|
|
goto errout_with_readbuf;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
|
|
|
|
/* Enable quad mode */
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[0] = sf25fl1_read_status1(priv);
|
|
|
|
|
priv->cmdbuf[1] = sf25fl1_read_status2(priv);
|
|
|
|
|
priv->cmdbuf[2] = sf25fl1_read_status3(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
while ((priv->cmdbuf[1] & STATUS2_QUAD_ENABLE_MASK) == 0)
|
2015-08-25 17:10:12 +02:00
|
|
|
|
{
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[1] |= STATUS2_QUAD_ENABLE;
|
2015-11-07 18:51:09 +01:00
|
|
|
|
s25fl1_write_status(priv);
|
2015-09-06 17:37:34 +02:00
|
|
|
|
priv->cmdbuf[1] = sf25fl1_read_status2(priv);
|
2015-08-26 22:16:45 +02:00
|
|
|
|
usleep(50*1000);
|
|
|
|
|
}
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-10 18:33:35 +01:00
|
|
|
|
/* Unprotect FLASH sectors if so requested. */
|
|
|
|
|
|
|
|
|
|
if (unprotect)
|
|
|
|
|
{
|
|
|
|
|
ret = s25fl1_unprotect(priv, 0, priv->nsectors - 1);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
fdbg("ERROR: Sector unprotect failed\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
#ifdef CONFIG_S25FL1_SECTOR512 /* Simulate a 512 byte sector */
|
2015-08-26 22:16:45 +02:00
|
|
|
|
/* Allocate a buffer for the erase block cache */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-11-10 20:34:33 +01:00
|
|
|
|
priv->sector = (FAR uint8_t *)QSPI_ALLOC(qspi, 1 << priv->sectorshift);
|
2015-09-06 17:37:34 +02:00
|
|
|
|
if (priv->sector == NULL)
|
2015-08-26 22:16:45 +02:00
|
|
|
|
{
|
|
|
|
|
/* Allocation failed! Discard all of that work we just did and return NULL */
|
2015-08-25 17:10:12 +02:00
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
fdbg("ERROR: Sector allocation failed\n");
|
2015-11-05 17:32:56 +01:00
|
|
|
|
goto errout_with_readbuf;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
2015-08-26 22:16:45 +02:00
|
|
|
|
#endif
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 15:16:07 +02:00
|
|
|
|
#ifdef CONFIG_MTD_REGISTRATION
|
2015-08-25 17:10:12 +02:00
|
|
|
|
/* Register the MTD with the procfs system if enabled */
|
|
|
|
|
|
2015-11-07 18:51:09 +01:00
|
|
|
|
mtd_register(&priv->mtd, "s25fl1");
|
2015-08-25 17:10:12 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Return the implementation-specific state structure as the MTD device */
|
|
|
|
|
|
|
|
|
|
fvdbg("Return %p\n", priv);
|
|
|
|
|
return (FAR struct mtd_dev_s *)priv;
|
2015-09-06 17:37:34 +02:00
|
|
|
|
|
2015-11-05 17:32:56 +01:00
|
|
|
|
errout_with_readbuf:
|
|
|
|
|
QSPI_FREE(qspi, priv->readbuf);
|
|
|
|
|
|
2015-09-06 17:37:34 +02:00
|
|
|
|
errout_with_cmdbuf:
|
|
|
|
|
QSPI_FREE(qspi, priv->cmdbuf);
|
|
|
|
|
|
|
|
|
|
errout_with_priv:
|
|
|
|
|
kmm_free(priv);
|
|
|
|
|
return NULL;
|
2015-08-25 17:10:12 +02:00
|
|
|
|
}
|