From 16dad06fc7d5f3137bd45ac340d8129abdadb928 Mon Sep 17 00:00:00 2001 From: armink Date: Sun, 9 Oct 2016 09:06:01 +0800 Subject: [PATCH] [DeviceDrivers]Add SFUD(Serial Flash Universal Driver) SPI Flash driver for using RTT device. It makes RT-Thread support more type SPI Flash. --- components/drivers/spi/SConscript | 6 + components/drivers/spi/sfud/LICENSE | 22 + components/drivers/spi/sfud/README.md | 235 +++++ components/drivers/spi/sfud/inc/sfud.h | 152 +++ components/drivers/spi/sfud/inc/sfud_cfg.h | 78 ++ components/drivers/spi/sfud/inc/sfud_def.h | 251 +++++ .../drivers/spi/sfud/inc/sfud_flash_def.h | 129 +++ components/drivers/spi/sfud/src/sfud.c | 916 ++++++++++++++++++ components/drivers/spi/sfud/src/sfud_sfdp.c | 389 ++++++++ components/drivers/spi/spi_flash_sfud.c | 551 +++++++++++ components/drivers/spi/spi_flash_sfud.h | 41 + 11 files changed, 2770 insertions(+) create mode 100644 components/drivers/spi/sfud/LICENSE create mode 100644 components/drivers/spi/sfud/README.md create mode 100644 components/drivers/spi/sfud/inc/sfud.h create mode 100644 components/drivers/spi/sfud/inc/sfud_cfg.h create mode 100644 components/drivers/spi/sfud/inc/sfud_def.h create mode 100644 components/drivers/spi/sfud/inc/sfud_flash_def.h create mode 100644 components/drivers/spi/sfud/src/sfud.c create mode 100644 components/drivers/spi/sfud/src/sfud_sfdp.c create mode 100644 components/drivers/spi/spi_flash_sfud.c create mode 100644 components/drivers/spi/spi_flash_sfud.h diff --git a/components/drivers/spi/SConscript b/components/drivers/spi/SConscript index e2baf6bab..63d7b9bae 100644 --- a/components/drivers/spi/SConscript +++ b/components/drivers/spi/SConscript @@ -23,6 +23,12 @@ if GetDepend('RT_USING_SST25VFXX'): if GetDepend('RT_USING_GD'): src_device += ['spi_flash_gd.c'] + +if GetDepend('RT_USING_SFUD'): + src_device += ['spi_flash_sfud.c', 'sfud/src/sfud.c'] + CPPPATH += [cwd + '/sfud/inc'] + if GetDepend('RT_SFUD_USING_SFDP'): + src_device += ['sfud/src/sfud_sfdp.c'] src += src_device diff --git a/components/drivers/spi/sfud/LICENSE b/components/drivers/spi/sfud/LICENSE new file mode 100644 index 000000000..6b761d3f4 --- /dev/null +++ b/components/drivers/spi/sfud/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Armink (armink.ztl@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/components/drivers/spi/sfud/README.md b/components/drivers/spi/sfud/README.md new file mode 100644 index 000000000..69ea4a67f --- /dev/null +++ b/components/drivers/spi/sfud/README.md @@ -0,0 +1,235 @@ +# SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库 + +--- + +## 0、SFUD 是什么 + +[SFUD](https://github.com/armink/SFUD) 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。 + +- 主要特点:面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址 +- 资源占用 + - 标准占用:RAM:0.2KB ROM:5.5KB + - 最小占用:RAM:0.1KB ROM:3.6KB +- 设计思路:这里要首先跟大家介绍一个标准: **SFDP** ,它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,最新版 V1.6B ([点击这里查看](https://www.jedec.org/standards-documents/docs/jesd216b))。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粗粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数,如果该 Flash 不支持 SFDP,则查询配置文件 ( `/sfud/inc/sfud_flash_def.h` ) 中提供的 **Flash 参数信息表** 中是否支持该款 Flash。如果不支持,则可以在配置文件中添加该款 Flash 的参数信息(添加方法详细见 [2.5 添加库目前不支持的 Flash](#25-添加库目前不支持的-flash))。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。 + +## 1、为什么选择 SFUD + +- 避免项目因 Flash 缺货、Flash 停产或产品扩容而带来的风险; +- 越来越多的项目将固件存储到串行 Flash 中,例如:ESP8266 的固件、主板中的 BIOS 及其他常见电子产品中的固件等等,但是各种 Flash 规格及命令不统一。使用 SFUD 即可避免,在相同功能的软件平台基础下,无法适配不同 Flash 种类的硬件平台的问题,提高软件的可重用性; +- 简化软件流程,降低开发难度。现在只需要配置好 SPI 通信,即可畅快的开始玩串行 Flash 了; +- 可以用来制作 Flash 编程器/烧写器 + +## 2、SFUD 如何使用 + +### 2.1 已支持 Flash + +下表为所有在 Demo 平台上进行过真机测试的 Flash。目前 SFUD 提供的 **Flash 参数信息表** 只包括下表中 **不支持** SFDP 标准的 Flash,其他不支持 SFDP 标准的 Flash 需要大家以后 **共同来完善和维护** **([Github](https://github.com/armink/SFUD)|[OSChina](http://git.oschina.net/armink/SFUD)|[Coding](https://coding.net/u/armink/p/SFUD/git))** 。如果觉得这个开源项目很赞,可以点击 [项目主页](https://github.com/armink/SFUD) 右上角的 **Star** ,同时把它推荐给更多有需要的朋友。 + +|型号|制造商|容量|最高速度|SFDP|备注| +|:--:|:----:|:--:|:--:|:--:|:--:| +|[W25Q40BV](http://microchip.ua/esp8266/W25Q40BV(EOL).pdf)|Winbond|4Mb|50Mhz|不支持|已停产| +|[W25Q80DV](http://www.winbond.com/resource-files/w25q80dv_revg_07212015.pdf)|Winbond|8Mb|104Mhz|支持|| +|[W25Q16CV](http://www.winbond.com/resource-files/da00-w25q16cvf1.pdf)|Winbond|16Mb|104Mhz|支持|| +|[W25Q32BV](http://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf)|Winbond|32Mb|104Mhz|支持|| +|[W25Q64CV](http://www.winbond.com/resource-files/w25q64cv_revh_052214[2].pdf)|Winbond|64Mb|80Mhz|支持|| +|[W25Q128BV](http://www.winbond.com/resource-files/w25q128bv_revh_100313_wo_automotive.pdf)|Winbond|128Mb|104Mhz|支持|| +|[W25Q256FV](http://www.winbond.com/resource-files/w25q256fv%20revi%2002262016%20kms.pdf)|Winbond|256Mb|104Mhz|支持|| +|[MX25L3206E](http://www.macronix.com/Lists/DataSheet/Attachments/3199/MX25L3206E,%203V,%2032Mb,%20v1.5.pdf)|Macronix|32Mb|86MHz|支持|| +|[KH25L3206E](http://www.macronix.com.hk/Lists/Datasheet/Attachments/131/KH25L3206E.pdf)|Macronix|32Mb|86Mhz|支持|| +|[SST25VF016B](http://ww1.microchip.com/downloads/en/DeviceDoc/20005044C.pdf)|Microchip|16Mb|50MHz|不支持| SST 已被 Microchip 收购| +|[M25P32](https://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p32.pdf)|Micron|32Mb|75Mhz|不支持|| +|[EN25Q32B](http://www.kean.com.au/oshw/WR703N/teardown/EN25Q32B%2032Mbit%20SPI%20Flash.pdf)|EON|32Mb|104MHz|不支持|| +|[GD25Q64B](http://www.gigadevice.com/product/download/24.html)|GigaDevice|64Mb|120Mhz|不支持|| +|[S25FL216K](http://www.cypress.com/file/197346/download)|Cypress|16Mb|65Mhz|不支持|| +|[S25FL164K](http://www.cypress.com/file/196886/download)|Cypress|64Mb|108Mhz|支持|| +|[A25LQ64](http://www.amictechnology.com/datasheets/A25LQ64.pdf)|AMIC|64Mb|104Mhz|支持|| +|[A25L080](http://www.amictechnology.com/datasheets/A25L080.pdf)|AMIC|8Mb|100Mhz|不支持|| +|[F25L004](http://www.esmt.com.tw/db/manager/upload/f25l004.pdf)|ESMT|4Mb|100Mhz|不支持|| +|[PCT25VF016B](http://pctgroup.com.tw/attachments/files/files/248_25VF016B-P.pdf)|PCT|16Mb|80Mhz|不支持|SST 授权许可,会被识别为 SST25VF016B| +|[AT45DB161E](http://www.adestotech.com/wp-content/uploads/doc8782.pdf)|ADESTO|16Mb|85MHz|不支持|ADESTO 收购 Atmel 串行闪存产品线| + +### 2.2 API 说明 + +#### 2.2.1 初始化 SFUD 库 + +```C +sfud_err sfud_init(void) +``` + +#### 2.2.2 获取 Flash 设备对象 + +在 SFUD 配置文件中会定义 Flash 设备表,负责存放所有将要使用的 Flash 设备对象,所以 SFUD 支持多个 Flash 设备同时驱动。设备表的配置在 `/sfud/inc/sfud_cfg.h` 中 `SFUD_FLASH_DEVICE_TABLE` 宏定义,详细配置方法参照 [2.3 配置方法 Flash](#23-配置方法))。本方法通过 Flash 设备位于设备表中索引值来返回 Flash 设备对象,超出设备表范围返回 `NULL` 。 + +```C +sfud_flash *sfud_get_device(size_t index) +``` + +|参数 |描述| +|:----- |:----| +|index |Flash 设备位于 FLash 设备表中的索引值| + +#### 2.2.3 获取 Flash 设备总数 + +返回 Flash 设备表的总长度。 + +```C +size_t sfud_get_device_num(void) +``` + +#### 2.2.4 获取 Flash 设备表 + +```C +const sfud_flash *sfud_get_device_table(void) +``` + +#### 2.2.5 读取 Flash 数据 + +```C +sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| +|addr |起始地址| +|size |读取数据的大小| +|data |读取到的数据| + +#### 2.2.6 擦除 Flash 数据 + +```C +sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| +|addr |起始地址| +|size |擦除数据的大小| + +#### 2.2.7 擦除 Flash 全部数据 + +```C +sfud_err sfud_chip_erase(const sfud_flash *flash) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| + +#### 2.2.8 往 Flash 写数据 + +```C +sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| +|addr |起始地址| +|size |写数据的大小| +|data |待写入的数据| + +#### 2.2.9 先擦除再往 Flash 写数据 + +```C +sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| +|addr |起始地址| +|size |写数据的大小| +|data |待写入的数据| + +#### 2.2.10 读取 Flash 状态 + +```C +sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| +|status |当前状态寄存器值| + +#### 2.2.11 写(修改) Flash 状态 + +```C +sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status) +``` + +|参数 |描述| +|:----- |:----| +|flash |Flash 设备对象| +|is_volatile |是否为易闪失的,true: 易闪失的,及断电后会丢失| +|status |当前状态寄存器值| + +### 2.3 配置方法 + +所有配置位于 `/sfud/inc/sfud_cfg.h` ,请参考下面的配置介绍,选择适合自己项目的配置。 + +#### 2.3.1 调试模式 + +打开/关闭 `SFUD_DEBUG_MODE` 宏定义 + +#### 2.3.2 是否使用 SFDP 参数功能 + +打开/关闭 `SFUD_USING_SFDP` 宏定义 + +> 注意:关闭后只会查询该库在 `/sfud/inc/sfud_flash_def.h` 中提供的 Flash 信息表。这样虽然会降低软件的适配性,但减少代码量。 + +#### 2.3.3 是否使用该库自带的 Flash 参数信息表 + +打开/关闭 `SFUD_USING_FLASH_INFO_TABLE` 宏定义 + +> 注意:关闭后该库只驱动支持 SFDP 规范的 Flash,也会适当的降低部分代码量。另外 2.3.2 及 2.3.3 这两个宏定义至少定义一种,也可以两种方式都选择。 + +#### 2.3.4 Flash 设备表 + +主要修改 `SFUD_FLASH_DEVICE_TABLE` 这个宏定义,示例如下: + +```C +enum { + SFUD_W25Q64CV_DEVICE_INDEX = 0, + SFUD_GD25Q64B_DEVICE_INDEX = 1, +}; + +#define SFUD_FLASH_DEVICE_TABLE \ +{ \ + [SFUD_W25Q64CV_DEVICE_INDEX] = {.name = "W25Q64CV", .spi.name = "SPI1"}, \ + [SFUD_GD25Q64B_DEVICE_INDEX] = {.name = "GD25Q64B", .spi.name = "SPI3"}, \ +} +``` + +上面定义了两个 Flash 设备(大部分产品一个足以),两个设备的名称为 `"W25Q64CV"` 及 `"GD25Q64B"` ,分别对应 `"SPI1"` 及 `"SPI3"` 这两个 SPI 设备名称(在移植 SPI 接口时会用到,位于 `/sfud/port/sfud_port.c` ), `SFUD_W25Q16CV_DEVICE_INDEX` 与 `SFUD_GD25Q64B_DEVICE_INDEX` 这两个枚举定义了两个设备位于设备表中的索引,可以通过 `sfud_get_device_table()` 方法获取到设备表,再配合这个索引值来访问指定的设备。 + +### 2.4 移植说明 + +移植文件位于 `/sfud/port/sfud_port.c` ,文件中的 `sfud_err sfud_spi_port_init(sfud_flash *flash)` 方法是库提供的移植方法,在里面完成各个设备 SPI 读写驱动(必选)、重试次数(必选)、重试接口(可选)及 SPI 锁(可选)的配置。更加详细的移植内容,可以参考 demo 中的各个平台的移植文件。 + +### 2.5 添加库目前不支持的 Flash + +这里需要修改 `/sfud/inc/sfdu_flash_def.h` ,所有已经支持的 Flash 见 `SFUD_FLASH_CHIP_TABLE` 宏定义,需要提前准备的 Flash 参数内容分别为:| 名称 | 制造商 ID | 类型 ID | 容量 ID | 容量 | 写模式 | 擦除粗粒度(擦除的最小单位) | 擦除粗粒度对应的命令 | 。这里以添加 兆易创新 ( GigaDevice ) 的 `GD25Q64B` Flash 来举例。 + +此款 Flash 为兆易创新的早期生产的型号,所以不支持 SFDP 标准。首先需要下载其数据手册,找到 0x9F 命令返回的 3 种 ID, 这里需要最后面两字节 ID ,即 `type id` 及 `capacity id` 。 `GD25Q64B` 对应这两个 ID 分别为 `0x40` 及 `0x17` 。上面要求的其他 Flash 参数都可以在数据手册中找到,这里要重点说明下 **写模式** 这个参数,库本身提供的写模式共计有 4 种,详见文件顶部的 `sfud_write_mode` 枚举类型,同一款 Flash 可以同时支持多种写模式,视情况而定。对于 `GD25Q64B` 而言,其支持的写模式应该为 `SFUD_WM_PAGE_256B` ,即写 1-256 字节每页。结合上述 `GD25Q64B` 的 Flash 参数应如下: + +``` + {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20}, +``` + +再将其增加到 `SFUD_FLASH_CHIP_TABLE` 宏定义末尾,即可完成该库对 `GD25Q64B` 的支持。 + +### 2.6 Demo + +目前已支持如下平台下的 Demo + +|路径 |平台描述| +|:----- |:----| +|[/demo/stm32f10x_non_os](https://github.com/armink/SFUD/tree/master/demo/stm32f10x_non_os) |STM32F10X 裸机平台| +|[/demo/stm32f2xx_rtt](https://github.com/armink/SFUD/tree/master/demo/stm32f2xx_rtt) |STM32F2XX + [RT-Thread](http://www.rt-thread.org/) 操作系统平台| + +### 2.7 许可 + +采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。 \ No newline at end of file diff --git a/components/drivers/spi/sfud/inc/sfud.h b/components/drivers/spi/sfud/inc/sfud.h new file mode 100644 index 000000000..1d7642ef9 --- /dev/null +++ b/components/drivers/spi/sfud/inc/sfud.h @@ -0,0 +1,152 @@ +/* + * This file is part of the Serial Flash Universal Driver Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. You can see all of the functions which can be called by user. + * Created on: 2016-04-23 + */ + +#ifndef _SFUD_H_ +#define _SFUD_H_ + +#include "sfud_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ../src/sfup.c */ +/** + * SFUD library initialize. + * + * @return result + */ +sfud_err sfud_init(void); + +/** + * get flash device by its index which in the flash information table + * + * @param index the index which in the flash information table @see flash_table + * + * @return flash device + */ +sfud_flash *sfud_get_device(size_t index); + +/** + * get flash device total number on flash device information table @see flash_table + * + * @return flash device total number + */ +size_t sfud_get_device_num(void); + +/** + * get flash device information table @see flash_table + * + * @return flash device table pointer + */ +const sfud_flash *sfud_get_device_table(void); + +/** + * read flash data + * + * @param flash flash device + * @param addr start address + * @param size read size + * @param data read data pointer + * + * @return result + */ +sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data); + +/** + * erase flash data + * + * @note It will erase align by erase granularity. + * + * @param flash flash device + * @param addr start address + * @param size erase size + * + * @return result + */ +sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size); + +/** + * write flash data (no erase operate) + * + * @param flash flash device + * @param addr start address + * @param data write data + * @param size write size + * + * @return result + */ +sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data); + +/** + * erase and write flash data + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data); + +/** + * erase all flash data + * + * @param flash flash device + * + * @return result + */ +sfud_err sfud_chip_erase(const sfud_flash *flash); + +/** + * read flash register status + * + * @param flash flash device + * @param status register status + * + * @return result + */ +sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status); + +/** + * write status register + * + * @param flash flash device + * @param is_volatile true: volatile mode, false: non-volatile mode + * @param status register status + * + * @return result + */ +sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status); + +#ifdef __cplusplus +} +#endif + +#endif /* _SFUD_H_ */ diff --git a/components/drivers/spi/sfud/inc/sfud_cfg.h b/components/drivers/spi/sfud/inc/sfud_cfg.h new file mode 100644 index 000000000..a0441dc53 --- /dev/null +++ b/components/drivers/spi/sfud/inc/sfud_cfg.h @@ -0,0 +1,78 @@ +/* + * This file is part of the Serial Flash Universal Driver Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this library. + * Created on: 2016-04-23 + */ + +#ifndef _SFUD_CFG_H_ +#define _SFUD_CFG_H_ + +#include + +/** + * It will print more information on debug mode. + * #define RT_DEBUG_SFUD 1: open debug mode */ +#if RT_DEBUG_SFUD +#define SFUD_DEBUG_MODE +#endif + +/** + * Using probe flash JEDEC SFDP parameter. + */ +#ifdef RT_SFUD_USING_SFDP +#define SFUD_USING_SFDP +#endif + +/** + * Using probe flash JEDEC ID then query defined supported flash chip information table. @see SFUD_FLASH_CHIP_TABLE + */ +#ifdef RT_SFUD_USING_FLASH_INFO_TABLE +#define SFUD_USING_FLASH_INFO_TABLE +#endif + +/** + * User defined flash device table. + * e.g. + * enum { + * SFUD_W25Q64_DEVICE_INDEX = 0, + * SFUD_SST25VF016B_DEVICE_INDEX = 1, + * }; + * #define RT_SFUD_FLASH_DEVICE_TABLE \ + * { \ + * [SFUD_W25Q64_DEVICE_INDEX] = {.name = "W25Q64", .spi.name = "spi10"}, \ + * [SFUD_SST25VF016B_DEVICE_INDEX] = {.name = "SST25VF016B", .spi.name = "spi30"}, \ + * } + */ +#ifndef RT_SFUD_FLASH_DEVICE_TABLE +#error "Please configure the flash device information table in (in rtconfig.h)." +#else +#define SFUD_FLASH_DEVICE_TABLE RT_SFUD_FLASH_DEVICE_TABLE +#endif + +#if !defined(RT_SFUD_USING_SFDP) && !defined(RT_SFUD_USING_FLASH_INFO_TABLE) +#error "Please configure RT_SFUD_USING_SFDP or RT_SFUD_USING_FLASH_INFO_TABLE at least one kind of mode (in rtconfig.h)." +#endif + +#endif /* _SFUD_CFG_H_ */ diff --git a/components/drivers/spi/sfud/inc/sfud_def.h b/components/drivers/spi/sfud/inc/sfud_def.h new file mode 100644 index 000000000..5bfa0c2f3 --- /dev/null +++ b/components/drivers/spi/sfud/inc/sfud_def.h @@ -0,0 +1,251 @@ +/* + * This file is part of the Serial Flash Universal Driver Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the macro definition head file for this library. + * Created on: 2016-04-23 + */ + +#ifndef _SFUD_DEF_H_ +#define _SFUD_DEF_H_ + +#include +#include +#include +#include +#include "sfud_flash_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* debug print function. Must be implement by user. */ +#ifdef SFUD_DEBUG_MODE +#define SFUD_DEBUG(...) sfud_log_debug(__FILE__, __LINE__, __VA_ARGS__) +#else +#define SFUD_DEBUG(...) +#endif + +#define SFUD_INFO(...) sfud_log_info(__VA_ARGS__) + +/* assert for developer. */ +#ifdef SFUD_DEBUG_MODE +#define SFUD_ASSERT(EXPR) \ +if (!(EXPR)) \ +{ \ + SFUD_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \ + while (1); \ +} +#else +#define SFUD_ASSERT(EXPR) +#endif + +/** + * retry process + * + * @param delay delay function for every retry. NULL will not delay for every retry. + * @param retry retry counts + * @param result SFUD_ERR_TIMEOUT: retry timeout + */ +#define SFUD_RETRY_PROCESS(delay, retry, result) \ + void (*__delay_temp)(void) = (void (*)(void))delay; \ + if (retry == 0) {result = SFUD_ERR_TIMEOUT;break;} \ + else {if (__delay_temp) {__delay_temp();} retry --;} + +/* software version number */ +#define SFUD_SW_VERSION "0.10.06" +/* + * all defined supported command + */ +#ifndef SFUD_CMD_WRITE_ENABLE +#define SFUD_CMD_WRITE_ENABLE 0x06 +#endif + +#ifndef SFUD_CMD_WRITE_DISABLE +#define SFUD_CMD_WRITE_DISABLE 0x04 +#endif + +#ifndef SFUD_CMD_READ_STATUS_REGISTER +#define SFUD_CMD_READ_STATUS_REGISTER 0x05 +#endif + +#ifndef SFUD_VOLATILE_SR_WRITE_ENABLE +#define SFUD_VOLATILE_SR_WRITE_ENABLE 0x50 +#endif + +#ifndef SFUD_CMD_WRITE_STATUS_REGISTER +#define SFUD_CMD_WRITE_STATUS_REGISTER 0x01 +#endif + +#ifndef SFUD_CMD_PAGE_PROGRAM +#define SFUD_CMD_PAGE_PROGRAM 0x02 +#endif + +#ifndef SFUD_CMD_AAI_WORD_PROGRAM +#define SFUD_CMD_AAI_WORD_PROGRAM 0xAD +#endif + +#ifndef SFUD_CMD_ERASE_CHIP +#define SFUD_CMD_ERASE_CHIP 0xC7 +#endif + +#ifndef SFUD_CMD_READ_DATA +#define SFUD_CMD_READ_DATA 0x03 +#endif + +#ifndef SFUD_CMD_MANUFACTURER_DEVICE_ID +#define SFUD_CMD_MANUFACTURER_DEVICE_ID 0x90 +#endif + +#ifndef SFUD_CMD_JEDEC_ID +#define SFUD_CMD_JEDEC_ID 0x9F +#endif + +#ifndef SFUD_CMD_READ_UNIQUE_ID +#define SFUD_CMD_READ_UNIQUE_ID 0x4B +#endif + +#ifndef SFUD_CMD_READ_SFDP_REGISTER +#define SFUD_CMD_READ_SFDP_REGISTER 0x5A +#endif + +#ifndef SFUD_CMD_ENABLE_RESET +#define SFUD_CMD_ENABLE_RESET 0x66 +#endif + +#ifndef SFUD_CMD_RESET +#define SFUD_CMD_RESET 0x99 +#endif + +#ifndef SFUD_CMD_ENTER_4B_ADDRESS_MODE +#define SFUD_CMD_ENTER_4B_ADDRESS_MODE 0xB7 +#endif + +#ifndef SFUD_CMD_EXIT_4B_ADDRESS_MODE +#define SFUD_CMD_EXIT_4B_ADDRESS_MODE 0xE9 +#endif + +#ifndef SFUD_WRITE_MAX_PAGE_SIZE +#define SFUD_WRITE_MAX_PAGE_SIZE 256 +#endif + +/* send dummy data for read data */ +#ifndef SFUD_DUMMY_DATA +#define SFUD_DUMMY_DATA 0xFF +#endif + +/* maximum number of erase type support on JESD216 (V1.0) */ +#define SFUD_SFDP_ERASE_TYPE_MAX_NUM 4 + +/** + * status register bits + */ +enum { + SFUD_STATUS_REGISTER_BUSY = (1 << 0), /**< busing */ + SFUD_STATUS_REGISTER_WEL = (1 << 1), /**< write enable latch */ + SFUD_STATUS_REGISTER_SRP = (1 << 7), /**< status register protect */ +}; + +/** + * error code + */ +typedef enum { + SFUD_SUCCESS = 0, /**< success */ + SFUD_ERR_NOT_FOUND = 1, /**< not found or not supported */ + SFUD_ERR_WRITE = 2, /**< write error */ + SFUD_ERR_READ = 3, /**< read error */ + SFUD_ERR_TIMEOUT = 4, /**< timeout error */ + SFUD_ERR_ADDR_OUT_OF_BOUND = 5, /**< address is out of flash bound */ +} sfud_err; + +/* SPI bus write read data function type */ +typedef sfud_err (*spi_write_read_func)(const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, size_t read_size); + +#ifdef SFUD_USING_SFDP +/** + * the SFDP (Serial Flash Discoverable Parameters) parameter info which used on this library + */ +typedef struct { + bool available; /**< available when read SFDP OK */ + uint8_t major_rev; /**< SFDP Major Revision */ + uint8_t minor_rev; /**< SFDP Minor Revision */ + uint16_t write_gran; /**< write granularity (bytes) */ + uint8_t erase_4k; /**< 4 kilobyte erase is supported throughout the device */ + uint8_t erase_4k_cmd; /**< 4 Kilobyte erase command */ + bool sr_is_non_vola; /**< status register is supports non-volatile */ + uint8_t vola_sr_we_cmd; /**< volatile status register write enable command */ + bool addr_3_byte; /**< supports 3-Byte addressing */ + bool addr_4_byte; /**< supports 4-Byte addressing */ + uint32_t capacity; /**< flash capacity (bytes) */ + struct { + uint32_t size; /**< erase sector size (bytes). 0x00: not available */ + uint8_t cmd; /**< erase command */ + } eraser[SFUD_SFDP_ERASE_TYPE_MAX_NUM]; /**< supported eraser types table */ + //TODO lots of fast read-related stuff (like modes supported and number of wait states/dummy cycles needed in each) +} sfud_sfdp, *sfud_sfdp_t; +#endif + +/** + * SPI device + */ +typedef struct __sfud_spi { + /* SPI device name */ + char *name; + /* SPI bus write read data function */ + sfud_err (*wr)(const struct __sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, + size_t read_size); + /* lock SPI bus */ + void (*lock)(const struct __sfud_spi *spi); + /* unlock SPI bus */ + void (*unlock)(const struct __sfud_spi *spi); + /* some user data */ + void *user_data; +} sfud_spi, *sfud_spi_t; + +/** + * serial flash device + */ +typedef struct { + char *name; /**< serial flash name */ + size_t index; /**< index of flash device information table @see flash_table */ + sfud_flash_chip chip; /**< flash chip information */ + sfud_spi spi; /**< SPI device */ + bool init_ok; /**< initialize OK flag */ + bool addr_in_4_byte; /**< flash is in 4-Byte addressing */ + struct { + void (*delay)(void); /**< every retry's delay */ + size_t times; /**< default times for error retry */ + } retry; + void *user_data; /**< some user data */ + +#ifdef SFUD_USING_SFDP + sfud_sfdp sfdp; /**< serial flash discoverable parameters by JEDEC standard */ +#endif + +} sfud_flash, *sfud_flash_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SFUD_DEF_H_ */ diff --git a/components/drivers/spi/sfud/inc/sfud_flash_def.h b/components/drivers/spi/sfud/inc/sfud_flash_def.h new file mode 100644 index 000000000..b593297df --- /dev/null +++ b/components/drivers/spi/sfud/inc/sfud_flash_def.h @@ -0,0 +1,129 @@ +/* + * This file is part of the Serial Flash Universal Driver Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the flash types and specification macro definition head file for this library. + * Created on: 2016-06-09 + */ + +#ifndef _SFUD_FLASH_DEF_H_ +#define _SFUD_FLASH_DEF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * flash program(write) data mode + */ +enum sfud_write_mode { + SFUD_WM_PAGE_256B = 1 << 0, /**< write 1 to 256 bytes per page */ + SFUD_WM_BYTE = 1 << 1, /**< byte write */ + SFUD_WM_AAI = 1 << 2, /**< auto address increment */ + SFUD_WM_DUAL_BUFFER = 1 << 3, /**< dual-buffer write, like AT45DB series */ +}; + +/* manufacturer information */ +typedef struct { + char *name; + uint8_t id; +} sfud_mf; + +/* flash chip information */ +typedef struct { + char *name; /**< flash chip name */ + uint8_t mf_id; /**< manufacturer ID */ + uint8_t type_id; /**< memory type ID */ + uint8_t capacity_id; /**< capacity ID */ + uint32_t capacity; /**< flash capacity (bytes) */ + uint16_t write_mode; /**< write mode @see sfud_write_mode */ + uint32_t erase_gran; /**< erase granularity (bytes) */ + uint8_t erase_gran_cmd; /**< erase granularity size block command */ +} sfud_flash_chip; + +/* SFUD support manufacturer JEDEC ID */ +#define SFUD_MF_ID_CYPRESS 0x01 +#define SFUD_MF_ID_FUJITSU 0x04 +#define SFUD_MF_ID_EON 0x1C +#define SFUD_MF_ID_ATMEL 0x1F +#define SFUD_MF_ID_MICRON 0x20 +#define SFUD_MF_ID_AMIC 0x37 +#define SFUD_MF_ID_SANYO 0x62 +#define SFUD_MF_ID_INTEL 0x89 +#define SFUD_MF_ID_ESMT 0x8C +#define SFUD_MF_ID_FUDAN 0xA1 +#define SFUD_MF_ID_HYUNDAI 0xAD +#define SFUD_MF_ID_SST 0xBF +#define SFUD_MF_ID_GIGADEVICE 0xC8 +#define SFUD_MF_ID_ISSI 0xD5 +#define SFUD_MF_ID_WINBOND 0xEF + +/* SFUD supported manufacturer information table */ +#define SFUD_MF_TABLE \ +{ \ + {"Cypress", SFUD_MF_ID_CYPRESS}, \ + {"Fujitsu", SFUD_MF_ID_FUJITSU}, \ + {"EON", SFUD_MF_ID_EON}, \ + {"Atmel", SFUD_MF_ID_ATMEL}, \ + {"Micron", SFUD_MF_ID_MICRON}, \ + {"AMIC", SFUD_MF_ID_AMIC}, \ + {"Sanyo", SFUD_MF_ID_SANYO}, \ + {"Intel", SFUD_MF_ID_INTEL}, \ + {"ESMT", SFUD_MF_ID_ESMT}, \ + {"Fudan", SFUD_MF_ID_FUDAN}, \ + {"Hyundai", SFUD_MF_ID_HYUNDAI}, \ + {"SST", SFUD_MF_ID_SST}, \ + {"GigaDevice", SFUD_MF_ID_GIGADEVICE}, \ + {"ISSI", SFUD_MF_ID_ISSI}, \ + {"Winbond", SFUD_MF_ID_WINBOND}, \ +} + +#ifdef SFUD_USING_FLASH_INFO_TABLE +/* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard, + * then the SFUD will find the flash chip information by this table. Developer can add other flash to here. + * The configuration information name and index reference the sfud_flash_chip structure. + * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd | + */ +#define SFUD_FLASH_CHIP_TABLE \ +{ \ + {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2*1024*1024, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \ + {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512*1024, SFUD_WM_PAGE_256B, 4096, 0x20}, \ + {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2*1024*1024, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \ + {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4*1024*1024, SFUD_WM_PAGE_256B, 64*1024, 0xD8}, \ + {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20}, \ + {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20}, \ + {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20}, \ + {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20}, \ + {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512*1024, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \ + {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2*1024*1024, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \ +} +#endif /* SFUD_USING_FLASH_INFO_TABLE */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SFUD_FLASH_DEF_H_ */ diff --git a/components/drivers/spi/sfud/src/sfud.c b/components/drivers/spi/sfud/src/sfud.c new file mode 100644 index 000000000..b7c04f4b0 --- /dev/null +++ b/components/drivers/spi/sfud/src/sfud.c @@ -0,0 +1,916 @@ +/* + * This file is part of the Serial Flash Universal Driver Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: serial flash operate functions by SFUD lib. + * Created on: 2016-04-23 + */ + +#include "../inc/sfud.h" +#include + +/* send dummy data for read data */ +#define DUMMY_DATA 0xFF + +#ifndef SFUD_FLASH_DEVICE_TABLE +#error "Please configure the flash device information table in (in sfud_cfg.h)." +#endif + +#if !defined(SFUD_USING_SFDP) && !defined(SFUD_USING_FLASH_INFO_TABLE) +#error "Please configure SFUD_USING_SFDP or SFUD_USING_FLASH_INFO_TABLE at least one kind of mode (in sfud_cfg.h)." +#endif + +/* user configured flash device information table */ +static sfud_flash flash_table[] = SFUD_FLASH_DEVICE_TABLE; +/* supported manufacturer information table */ +static const sfud_mf mf_table[] = SFUD_MF_TABLE; + +#ifdef SFUD_USING_FLASH_INFO_TABLE +/* supported flash chip information table */ +static const sfud_flash_chip flash_chip_table[] = SFUD_FLASH_CHIP_TABLE; +#endif + +static sfud_err software_init(const sfud_flash *flash); +static sfud_err hardware_init(sfud_flash *flash); +static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size, uint16_t write_gran, + const uint8_t *data); +static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data); +static sfud_err wait_busy(const sfud_flash *flash); +static sfud_err reset(const sfud_flash *flash); +static sfud_err read_jedec_id(sfud_flash *flash); +static sfud_err set_write_enabled(const sfud_flash *flash, bool enabled); +static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled); +static void make_adress_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array); + +/* ../port/sfup_port.c */ +extern void sfud_log_debug(const char *file, const long line, const char *format, ...); +extern void sfud_log_info(const char *format, ...); + +/** + * SFUD initialize by flash device + * + * @param flash flash device + * + * @return result + */ +sfud_err sfud_device_init(sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + + /* hardware initialize */ + result = hardware_init(flash); + if (result == SFUD_SUCCESS) { + result = software_init(flash); + } + if (result == SFUD_SUCCESS) { + flash->init_ok = true; + SFUD_INFO("%s flash device is initialize success.", flash->name); + } else { + flash->init_ok = false; + SFUD_INFO("Error: %s flash device is initialize fail.", flash->name); + } + + return result; +} + +/** + * SFUD library initialize. + * + * @return result + */ +sfud_err sfud_init(void) { + sfud_err cur_flash_result = SFUD_SUCCESS, all_flash_result = SFUD_SUCCESS; + size_t i; + + SFUD_DEBUG("Start initialize Serial Flash Universal Driver(SFUD) V%s.", SFUD_SW_VERSION); + SFUD_DEBUG("You can get the latest version on https://github.com/armink/SFUD ."); + /* initialize all flash device in flash device table */ + for (i = 0; i < sizeof(flash_table) / sizeof(sfud_flash); i++) { + /* initialize flash device index of flash device information table */ + flash_table[i].index = i; + cur_flash_result = sfud_device_init(&flash_table[i]); + + if (cur_flash_result != SFUD_SUCCESS) { + cur_flash_result = cur_flash_result; + } + } + + return all_flash_result; +} + +/** + * get flash device by its index which in the flash information table + * + * @param index the index which in the flash information table @see flash_table + * + * @return flash device + */ +sfud_flash *sfud_get_device(size_t index) { + if (index < sfud_get_device_num()) { + return &flash_table[index]; + } else { + return NULL; + } +} + +/** + * get flash device total number on flash device information table @see flash_table + * + * @return flash device total number + */ +size_t sfud_get_device_num(void) { + return sizeof(flash_table) / sizeof(sfud_flash); +} + +/** + * get flash device information table @see flash_table + * + * @return flash device table pointer + */ +const sfud_flash *sfud_get_device_table(void) { + return flash_table; +} + +/** + * hardware initialize + */ +static sfud_err hardware_init(sfud_flash *flash) { + extern sfud_err sfud_spi_port_init(sfud_flash *flash); + + sfud_err result = SFUD_SUCCESS; + size_t i; + + SFUD_ASSERT(flash); + + result = sfud_spi_port_init(flash); + if (result != SFUD_SUCCESS) { + return result; + } + + /* SPI write read function must be initialize */ + SFUD_ASSERT(flash->spi.wr); + /* if the user don't configure flash chip information then using SFDP parameter or static flash parameter table */ + if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0 + || flash->chip.erase_gran_cmd == 0) { + /* read JEDEC ID include manufacturer ID, memory type ID and flash capacity ID */ + result = read_jedec_id(flash); + if (result != SFUD_SUCCESS) { + return result; + } + +#ifdef SFUD_USING_SFDP + extern bool sfud_read_sfdp(sfud_flash *flash); + /* read SFDP parameters */ + if (sfud_read_sfdp(flash)) { + flash->chip.name = NULL; + flash->chip.capacity = flash->sfdp.capacity; + /* only 1 byte or 256 bytes write mode for SFDP */ + if (flash->sfdp.write_gran == 1) { + flash->chip.write_mode = SFUD_WM_BYTE; + } else { + flash->chip.write_mode = SFUD_WM_PAGE_256B; + } + /* find the the smallest erase sector size for eraser. then will use this size for erase granularity */ + flash->chip.erase_gran = flash->sfdp.eraser[0].size; + flash->chip.erase_gran_cmd = flash->sfdp.eraser[0].cmd; + for (i = 1; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) { + if (flash->sfdp.eraser[i].size != 0 && flash->chip.erase_gran > flash->sfdp.eraser[i].size) { + flash->chip.erase_gran = flash->sfdp.eraser[i].size; + flash->chip.erase_gran_cmd = flash->sfdp.eraser[i].cmd; + } + } + } else { +#endif + +#ifdef SFUD_USING_FLASH_INFO_TABLE + /* read SFDP parameters failed then using SFUD library provided static parameter */ + for (i = 0; i < sizeof(flash_chip_table) / sizeof(sfud_flash_chip); i++) { + if ((flash_chip_table[i].mf_id == flash->chip.mf_id) + && (flash_chip_table[i].type_id == flash->chip.type_id) + && (flash_chip_table[i].capacity_id == flash->chip.capacity_id)) { + flash->chip.name = flash_chip_table[i].name; + flash->chip.capacity = flash_chip_table[i].capacity; + flash->chip.write_mode = flash_chip_table[i].write_mode; + flash->chip.erase_gran = flash_chip_table[i].erase_gran; + flash->chip.erase_gran_cmd = flash_chip_table[i].erase_gran_cmd; + break; + } + } +#endif + +#ifdef SFUD_USING_SFDP + } +#endif + + } + + if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0 + || flash->chip.erase_gran_cmd == 0) { + SFUD_INFO("Warning: This flash device is not found or not support."); + return SFUD_ERR_NOT_FOUND; + } else { + const char *flash_mf_name = NULL; + /* find the manufacturer information */ + for (i = 0; i < sizeof(mf_table) / sizeof(sfud_mf); i++) { + if (mf_table[i].id == flash->chip.mf_id) { + flash_mf_name = mf_table[i].name; + break; + } + } + /* print manufacturer and flash chip name */ + if (flash_mf_name && flash->chip.name) { + SFUD_INFO("Find a %s %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.name, + flash->chip.capacity); + } else if (flash_mf_name) { + SFUD_INFO("Find a %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.capacity); + } + } + + /* reset flash device */ + result = reset(flash); + if (result != SFUD_SUCCESS) { + return result; + } + + /* I found when the flash read mode is supported AAI mode. The flash all blocks is protected, + * so need change the flash status to unprotected before write and erase operate. */ + if (flash->chip.write_mode & SFUD_WM_AAI) { + result = sfud_write_status(flash, true, 0x00); + if (result != SFUD_SUCCESS) { + return result; + } + } + + /* if the flash is large than 16MB (256Mb) then enter in 4-Byte addressing mode */ + if (flash->chip.capacity > (1 << 24)) { + result = set_4_byte_address_mode(flash, true); + } else { + flash->addr_in_4_byte = false; + } + + return result; +} + +/** + * software initialize + * + * @param flash flash device + * + * @return result + */ +static sfud_err software_init(const sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + + SFUD_ASSERT(flash); + + return result; +} + +/** + * read flash data + * + * @param flash flash device + * @param addr start address + * @param size read size + * @param data read data pointer + * + * @return result + */ +sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[5], cmd_size; + + SFUD_ASSERT(flash); + SFUD_ASSERT(data); + /* must be call this function after initialize OK */ + SFUD_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + SFUD_INFO("Error: Flash address is out of bound."); + return SFUD_ERR_ADDR_OUT_OF_BOUND; + } + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + result = wait_busy(flash); + + if (result == SFUD_SUCCESS) { + cmd_data[0] = SFUD_CMD_READ_DATA; + make_adress_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + result = spi->wr(spi, cmd_data, cmd_size, data, size); + } + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + + +/** + * erase all flash data + * + * @param flash flash device + * + * @return result + */ +sfud_err sfud_chip_erase(const sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[4]; + + SFUD_ASSERT(flash); + /* must be call this function after initialize OK */ + SFUD_ASSERT(flash->init_ok); + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != SFUD_SUCCESS) { + goto exit; + } + + cmd_data[0] = SFUD_CMD_ERASE_CHIP; + /* dual-buffer write, like AT45DB series flash chip erase operate is different for other flash */ + if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER) { + cmd_data[1] = 0x94; + cmd_data[2] = 0x80; + cmd_data[3] = 0x9A; + result = spi->wr(spi, cmd_data, 4, NULL, 0); + } else { + result = spi->wr(spi, cmd_data, 1, NULL, 0); + } + if (result != SFUD_SUCCESS) { + SFUD_INFO("Error: Flash chip erase SPI communicate error."); + goto exit; + } + result = wait_busy(flash); + +exit: + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * erase flash data + * + * @note It will erase align by erase granularity. + * + * @param flash flash device + * @param addr start address + * @param size erase size + * + * @return result + */ +sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size) { + extern size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr, size_t erase_size); + + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[5], cmd_size, cur_erase_cmd; + size_t eraser_index, cur_erase_size; + + SFUD_ASSERT(flash); + /* must be call this function after initialize OK */ + SFUD_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + SFUD_INFO("Error: Flash address is out of bound."); + return SFUD_ERR_ADDR_OUT_OF_BOUND; + } + + if (addr == 0 && size == flash->chip.capacity) { + return sfud_chip_erase(flash); + } + + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* loop erase operate. erase unit is erase granularity */ + while (size) { + /* if this flash is support SFDP parameter, then used SFDP parameter supplies eraser */ +#ifdef SFUD_USING_SFDP + if (flash->sfdp.available) { + /* get the suitable eraser for erase process from SFDP parameter */ + eraser_index = sfud_sfdp_get_suitable_eraser(flash, addr, size); + cur_erase_cmd = flash->sfdp.eraser[eraser_index].cmd; + cur_erase_size = flash->sfdp.eraser[eraser_index].size; + } else { +#else + { +#endif + cur_erase_cmd = flash->chip.erase_gran_cmd; + cur_erase_size = flash->chip.erase_gran; + } + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != SFUD_SUCCESS) { + break; + } + + cmd_data[0] = cur_erase_cmd; + make_adress_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + result = spi->wr(spi, cmd_data, cmd_size, NULL, 0); + if (result != SFUD_SUCCESS) { + SFUD_INFO("Error: Flash erase SPI communicate error."); + break; + } + result = wait_busy(flash); + if (result != SFUD_SUCCESS) { + break; + } + /* make erase align and calculate next erase address */ + if (addr % cur_erase_size != 0) { + if (size > cur_erase_size - (addr % cur_erase_size)) { + size -= cur_erase_size - (addr % cur_erase_size); + addr += cur_erase_size - (addr % cur_erase_size); + } else { + break; + } + } else { + if (size > cur_erase_size) { + size -= cur_erase_size; + addr += cur_erase_size; + } else { + break; + } + } + } + + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * write flash data (no erase operate) for write 1 to 256 bytes per page mode or byte write mode + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param write_gran write granularity bytes, only support 1 or 256 + * @param data write data + * + * @return result + */ +static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size, uint16_t write_gran, + const uint8_t *data) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[5 + SFUD_WRITE_MAX_PAGE_SIZE], cmd_size; + size_t data_size; + + SFUD_ASSERT(flash); + /* only support 1 or 256 */ + SFUD_ASSERT(write_gran == 1 || write_gran == 256); + /* must be call this function after initialize OK */ + SFUD_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + SFUD_INFO("Error: Flash address is out of bound."); + return SFUD_ERR_ADDR_OUT_OF_BOUND; + } + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* loop write operate. write unit is write granularity */ + while (size) { + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != SFUD_SUCCESS) { + break; + } + cmd_data[0] = SFUD_CMD_PAGE_PROGRAM; + make_adress_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + + /* make write align and calculate next write address */ + if (addr % write_gran != 0) { + if (size > write_gran - (addr % write_gran)) { + data_size = write_gran - (addr % write_gran); + } else { + data_size = size; + } + } else { + if (size > write_gran) { + data_size = write_gran; + } else { + data_size = size; + } + } + size -= data_size; + addr += data_size; + + memcpy(&cmd_data[cmd_size], data, data_size); + + result = spi->wr(spi, cmd_data, cmd_size + data_size, NULL, 0); + if (result != SFUD_SUCCESS) { + SFUD_INFO("Error: Flash write SPI communicate error."); + break; + } + result = wait_busy(flash); + if (result != SFUD_SUCCESS) { + break; + } + data += data_size; + } + + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * write flash data (no erase operate) for auto address increment mode + * + * If the address is odd number, it will place one 0xFF before the start of data for protect the old data. + * If the latest remain size is 1, it will append one 0xFF at the end of data for protect the old data. + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[6], cmd_size; + const size_t data_size = 2; + bool first_write = true; + + SFUD_ASSERT(flash); + SFUD_ASSERT(size >= 2); + /* must be call this function after initialize OK */ + SFUD_ASSERT(flash->init_ok); + /* check the flash address bound */ + if (addr + size > flash->chip.capacity) { + SFUD_INFO("Error: Flash address is out of bound."); + return SFUD_ERR_ADDR_OUT_OF_BOUND; + } + /* lock SPI */ + if (spi->lock) { + spi->lock(spi); + } + + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != SFUD_SUCCESS) { + goto exit; + } + /* loop write operate. write unit is write granularity */ + cmd_data[0] = SFUD_CMD_AAI_WORD_PROGRAM; + while (size) { + if (first_write) { + make_adress_byte_array(flash, addr, &cmd_data[1]); + cmd_size = flash->addr_in_4_byte ? 5 : 4; + if (addr % 2 == 0) { + cmd_data[cmd_size] = *data; + cmd_data[cmd_size + 1] = *(data + 1); + } else { + cmd_data[cmd_size] = 0xFF; + cmd_data[cmd_size + 1] = *data; + size++; + data--; + } + first_write = false; + } else { + cmd_size = 1; + if (size != 1) { + cmd_data[1] = *data; + cmd_data[2] = *(data + 1); + } else { + cmd_data[1] = *data; + cmd_data[2] = 0xFF; + size++; + } + } + + result = spi->wr(spi, cmd_data, cmd_size + data_size, NULL, 0); + if (result != SFUD_SUCCESS) { + SFUD_INFO("Error: Flash write SPI communicate error."); + goto exit; + } + + result = wait_busy(flash); + if (result != SFUD_SUCCESS) { + goto exit; + } + + size -= 2; + data += data_size; + } + /* set the flash write disable */ + result = set_write_enabled(flash, false); + + exit: + /* unlock SPI */ + if (spi->unlock) { + spi->unlock(spi); + } + + return result; +} + +/** + * write flash data (no erase operate) + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) { + sfud_err result = SFUD_SUCCESS; + + if (flash->chip.write_mode & SFUD_WM_PAGE_256B) { + result = page256_or_1_byte_write(flash, addr, size, 256, data); + } else if (flash->chip.write_mode & SFUD_WM_AAI) { + result = aai_write(flash, addr, size, data); + } else if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER) { + //TODO dual-buffer write mode + } + + return result; +} + +/** + * erase and write flash data + * + * @param flash flash device + * @param addr start address + * @param size write size + * @param data write data + * + * @return result + */ +sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data) { + sfud_err result = SFUD_SUCCESS; + + result = sfud_erase(flash, addr, size); + + if (result == SFUD_SUCCESS) { + result = sfud_write(flash, addr, size, data); + } + + return result; +} + +static sfud_err reset(const sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[2]; + + SFUD_ASSERT(flash); + + cmd_data[0] = SFUD_CMD_ENABLE_RESET; + cmd_data[1] = SFUD_CMD_RESET; + result = spi->wr(spi, cmd_data, 2, NULL, 0); + + if (result == SFUD_SUCCESS) { + result = wait_busy(flash); + } + + if (result == SFUD_SUCCESS) { + SFUD_DEBUG("Flash device reset success."); + } else { + SFUD_INFO("Error: Flash device reset failed."); + } + + return result; +} + +static sfud_err read_jedec_id(sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[1], recv_data[3]; + + SFUD_ASSERT(flash); + + cmd_data[0] = SFUD_CMD_JEDEC_ID; + result = spi->wr(spi, cmd_data, sizeof(cmd_data), recv_data, sizeof(recv_data)); + if (result == SFUD_SUCCESS) { + flash->chip.mf_id = recv_data[0]; + flash->chip.type_id = recv_data[1]; + flash->chip.capacity_id = recv_data[2]; + SFUD_DEBUG("The flash device manufacturer ID is 0x%02X, memory type ID is 0x%02X, capacity ID is 0x%02X.", + flash->chip.mf_id, flash->chip.type_id, flash->chip.capacity_id); + } else { + SFUD_INFO("Error: Read flash device JEDEC ID error."); + } + + return result; +} + +/** + * set the flash write enable or write disable + * + * @param flash flash device + * @param enabled true: enable false: disable + * + * @return result + */ +static sfud_err set_write_enabled(const sfud_flash *flash, bool enabled) { + sfud_err result = SFUD_SUCCESS; + uint8_t cmd, register_status; + + SFUD_ASSERT(flash); + + if (enabled) { + cmd = SFUD_CMD_WRITE_ENABLE; + } else { + cmd = SFUD_CMD_WRITE_DISABLE; + } + + result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0); + + if (result == SFUD_SUCCESS) { + result = sfud_read_status(flash, ®ister_status); + } + + if (result == SFUD_SUCCESS) { + if (enabled && (register_status & SFUD_STATUS_REGISTER_WEL) == 0) { + SFUD_INFO("Error: Can't enable write status."); + return SFUD_ERR_WRITE; + } else if (!enabled && (register_status & SFUD_STATUS_REGISTER_WEL) == 1) { + SFUD_INFO("Error: Can't disable write status."); + return SFUD_ERR_WRITE; + } + } + + return result; +} + +/** + * enable or disable 4-Byte addressing for flash + * + * @note The 4-Byte addressing just supported for the flash capacity which is large then 16MB (256Mb). + * + * @param flash flash device + * @param enabled true: enable false: disable + * + * @return result + */ +static sfud_err set_4_byte_address_mode(sfud_flash *flash, bool enabled) { + sfud_err result = SFUD_SUCCESS; + uint8_t cmd; + + SFUD_ASSERT(flash); + + /* set the flash write enable */ + result = set_write_enabled(flash, true); + if (result != SFUD_SUCCESS) { + return result; + } + + if (enabled) { + cmd = SFUD_CMD_ENTER_4B_ADDRESS_MODE; + } else { + cmd = SFUD_CMD_EXIT_4B_ADDRESS_MODE; + } + + result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0); + + if (result == SFUD_SUCCESS) { + flash->addr_in_4_byte = enabled ? true : false; + SFUD_DEBUG("%s 4-Byte addressing mode success.", enabled ? "Enter" : "Exit"); + } else { + SFUD_INFO("Error: %s 4-Byte addressing mode failed.", enabled ? "Enter" : "Exit"); + } + + return result; +} + +/** + * read flash register status + * + * @param flash flash device + * @param status register status + * + * @return result + */ +sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status) { + uint8_t cmd = SFUD_CMD_READ_STATUS_REGISTER; + + SFUD_ASSERT(flash); + SFUD_ASSERT(status); + + return flash->spi.wr(&flash->spi, &cmd, 1, status, 1); +} + +static sfud_err wait_busy(const sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + uint8_t status; + size_t retry_times = flash->retry.times; + + SFUD_ASSERT(flash); + + while (true) { + result = sfud_read_status(flash, &status); + if (result == SFUD_SUCCESS && ((status & SFUD_STATUS_REGISTER_BUSY)) == 0) { + break; + } + /* retry counts */ + SFUD_RETRY_PROCESS(flash->retry.delay, retry_times, result); + } + + if (result != SFUD_SUCCESS || ((status & SFUD_STATUS_REGISTER_BUSY)) != 0) { + SFUD_INFO("Error: Flash wait busy has an error."); + } + + return result; +} + +static void make_adress_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array) { + uint8_t len, i; + + SFUD_ASSERT(flash); + SFUD_ASSERT(array); + + len = flash->addr_in_4_byte ? 4 : 3; + + for (i = 0; i < len; i++) { + array[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF; + } +} + +/** + * write status register + * + * @param flash flash device + * @param is_volatile true: volatile mode, false: non-volatile mode + * @param status register status + * + * @return result + */ +sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status) { + sfud_err result = SFUD_SUCCESS; + const sfud_spi *spi = &flash->spi; + uint8_t cmd_data[2]; + + SFUD_ASSERT(flash); + + if (is_volatile) { + cmd_data[0] = SFUD_VOLATILE_SR_WRITE_ENABLE; + result = spi->wr(spi, cmd_data, 1, NULL, 0); + } else { + result = set_write_enabled(flash, true); + } + + if (result == SFUD_SUCCESS) { + cmd_data[0] = SFUD_CMD_WRITE_STATUS_REGISTER; + cmd_data[1] = status; + result = spi->wr(spi, cmd_data, 2, NULL, 0); + } + + if (result != SFUD_SUCCESS) { + SFUD_INFO("Error: Write_status register failed."); + } + + return result; +} diff --git a/components/drivers/spi/sfud/src/sfud_sfdp.c b/components/drivers/spi/sfud/src/sfud_sfdp.c new file mode 100644 index 000000000..7d37e11e8 --- /dev/null +++ b/components/drivers/spi/sfud/src/sfud_sfdp.c @@ -0,0 +1,389 @@ +/* + * This file is part of the Serial Flash Universal Driver Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Analyze the SFDP (Serial Flash Discoverable Parameters) which from JESD216/A/B (V1.X) standard. + * JESD216 (V1.0) document: http://www.jedec.org/sites/default/files/docs/JESD216.pdf + * JESD216A (V1.5) document: http://www.jedec.org/sites/default/files/docs/JESD216A.pdf + * JESD216B (V1.6) document: http://www.jedec.org/sites/default/files/docs/JESD216B.pdf + * + * Created on: 2016-05-26 + */ + +#include "../inc/sfud.h" + +/** + * JEDEC Standard JESD216 Terms and definitions: + * + * DWORD: Four consecutive 8-bit bytes used as the basic 32-bit building block for headers and parameter tables. + * + * Sector: The minimum granularity - size and alignment - of an area that can be erased in the data array + * of a flash memory device. Different areas within the address range of the data array may have a different + * minimum erase granularity (sector size). + */ + +#ifdef SFUD_USING_SFDP + +/* support maximum SFDP major revision by driver */ +#define SUPPORT_MAX_SFDP_MAJOR_REV 1 +/* the JEDEC basic flash parameter table length is 9 DWORDs (288-bit) on JESD216 (V1.0) initial release standard */ +#define BASIC_TABLE_LEN 9 +/* the smallest eraser in SFDP eraser table */ +#define SMALLEST_ERASER_INDEX 0 +/** + * SFDP parameter header structure + */ +typedef struct { + uint8_t id; /**< Parameter ID LSB */ + uint8_t minor_rev; /**< Parameter minor revision */ + uint8_t major_rev; /**< Parameter major revision */ + uint8_t len; /**< Parameter table length(in double words) */ + uint32_t ptp; /**< Parameter table 24bit pointer (byte address) */ +} sfdp_para_header; + +static sfud_err read_sfdp_data(const sfud_flash *flash, uint32_t addr, uint8_t *read_buf, size_t size); +static bool read_sfdp_header(sfud_flash *flash); +static bool read_basic_header(const sfud_flash *flash, sfdp_para_header *basic_header); +static bool read_basic_table(sfud_flash *flash, sfdp_para_header *basic_header); + +/* ../port/sfup_port.c */ +extern void sfud_log_debug(const char *file, const long line, const char *format, ...); +extern void sfud_log_info(const char *format, ...); + +/** + * Read SFDP parameter information + * + * @param flash flash device + * + * @return true: read OK + */ +bool sfud_read_sfdp(sfud_flash *flash) { + SFUD_ASSERT(flash); + + /* JEDEC basic flash parameter header */ + sfdp_para_header basic_header; + if (read_sfdp_header(flash) && read_basic_header(flash, &basic_header)) { + return read_basic_table(flash, &basic_header); + } else { + SFUD_INFO("Warning: Read SFDP parameter header information failed. The %s is not support JEDEC SFDP.", flash->name); + return false; + } +} + +/** + * Read SFDP parameter header + * + * @param flash flash device + * + * @return true: read OK + */ +static bool read_sfdp_header(sfud_flash *flash) { + sfud_sfdp *sfdp = &flash->sfdp; + /* The SFDP header is located at address 000000h of the SFDP data structure. + * It identifies the SFDP Signature, the number of parameter headers, and the SFDP revision numbers. */ + /* sfdp parameter header address */ + uint32_t header_addr = 0; + /* each parameter header being 2 DWORDs (64-bit) */ + uint8_t header[2 * 4] = { 0 }; + /* number of parameter headers */ + uint8_t npn = 0; + + SFUD_ASSERT(flash); + + sfdp->available = false; + /* read SFDP header */ + if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != SFUD_SUCCESS) { + SFUD_INFO("Error: Can't read SFDP header."); + return false; + } + /* check SFDP header */ + if (!(header[0] == 'S' && + header[1] == 'F' && + header[2] == 'D' && + header[3] == 'P')) { + SFUD_INFO("Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P')."); + return false; + } + sfdp->minor_rev = header[4]; + sfdp->major_rev = header[5]; + npn = header[6]; + if (sfdp->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) { + SFUD_INFO("Error: This reversion(V%d.%d) SFDP is not supported.", sfdp->major_rev, sfdp->minor_rev); + return false; + } + SFUD_DEBUG("Check SFDP header is OK. The reversion is V%d.%d, NPN is %d.", sfdp->major_rev, sfdp->minor_rev, npn); + + return true; +} + +/** + * Read JEDEC basic parameter header + * + * @param flash flash device + * + * @return true: read OK + */ +static bool read_basic_header(const sfud_flash *flash, sfdp_para_header *basic_header) { + /* The basic parameter header is mandatory, is defined by this standard, and starts at byte offset 08h. */ + uint32_t header_addr = 8; + /* each parameter header being 2 DWORDs (64-bit) */ + uint8_t header[2 * 4] = { 0 }; + + SFUD_ASSERT(flash); + SFUD_ASSERT(basic_header); + + /* read JEDEC basic flash parameter header */ + if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != SFUD_SUCCESS) { + SFUD_INFO("Error: Can't read JEDEC basic flash parameter header."); + return false; + } + basic_header->id = header[0]; + basic_header->minor_rev = header[1]; + basic_header->major_rev = header[2]; + basic_header->len = header[3]; + basic_header->ptp = header[4] | header[5] << 8 | header[6] << 16; + /* check JEDEC basic flash parameter header */ + if (basic_header->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) { + SFUD_INFO("Error: This reversion(V%d.%d) JEDEC basic flash parameter header is not supported.", + basic_header->major_rev, basic_header->minor_rev); + return false; + } + if (basic_header->len < BASIC_TABLE_LEN) { + SFUD_INFO("Error: The JEDEC basic flash parameter table length (now is %d) error.", basic_header->len); + return false; + } + SFUD_DEBUG("Check JEDEC basic flash parameter header is OK. The table id is %d, reversion is V%d.%d," + " length is %d, parameter table pointer is 0x%06X.", basic_header->id, basic_header->major_rev, + basic_header->minor_rev, basic_header->len, basic_header->ptp); + + return true; +} + +/** + * Read JEDEC basic parameter table + * + * @param flash flash device + * + * @return true: read OK + */ +static bool read_basic_table(sfud_flash *flash, sfdp_para_header *basic_header) { + sfud_sfdp *sfdp = &flash->sfdp; + /* parameter table address */ + uint32_t table_addr = basic_header->ptp; + /* parameter table */ + uint8_t table[BASIC_TABLE_LEN * 4] = { 0 }, i, j; + + SFUD_ASSERT(flash); + SFUD_ASSERT(basic_header); + + /* read JEDEC basic flash parameter table */ + if (read_sfdp_data(flash, table_addr, table, sizeof(table)) != SFUD_SUCCESS) { + SFUD_INFO("Error: Can't read JEDEC basic flash parameter table."); + return false; + } + /* print JEDEC basic flash parameter table info */ + SFUD_DEBUG("JEDEC basic flash parameter table info:"); + SFUD_DEBUG("MSB-LSB 3 2 1 0"); + for (i = 0; i < BASIC_TABLE_LEN; i++) { + SFUD_DEBUG("[%04d] 0x%02X 0x%02X 0x%02X 0x%02X", i + 1, table[i * 4 + 3], table[i * 4 + 2], table[i * 4 + 1], + table[i * 4]); + } + + /* get block/sector 4 KB erase supported and command */ + sfdp->erase_4k_cmd = table[1]; + switch (table[0] & 0x03) { + case 1: + sfdp->erase_4k = true; + SFUD_DEBUG("4 KB Erase is supported throughout the device. Command is 0x%02X.", sfdp->erase_4k_cmd); + break; + case 3: + sfdp->erase_4k = false; + SFUD_DEBUG("Uniform 4 KB erase is unavailable for this device."); + break; + default: + SFUD_INFO("Error: Uniform 4 KB erase supported information error."); + return false; + } + /* get write granularity */ + //TODO ĿǰΪ 1.0 ṩķʽ֧ V1.5 ϵķʽȡ page size + switch ((table[0] & (0x01 << 2)) >> 2) { + case 0: + sfdp->write_gran = 1; + SFUD_DEBUG("Write granularity is 1 byte."); + break; + case 1: + sfdp->write_gran = 256; + SFUD_DEBUG("Write granularity is 64 bytes or larger."); + break; + } + /* volatile status register block protect bits */ + switch ((table[0] & (0x01 << 3)) >> 3) { + case 0: + /* Block Protect bits in device's status register are solely non-volatile or may be + * programmed either as volatile using the 50h instruction for write enable or non-volatile + * using the 06h instruction for write enable. + */ + sfdp->sr_is_non_vola = true; + SFUD_DEBUG("Target flash status register is non-volatile."); + break; + case 1: + /* block protect bits in device's status register are solely volatile. */ + sfdp->sr_is_non_vola = false; + SFUD_DEBUG("Block Protect bits in device's status register are solely volatile."); + /* write enable instruction select for writing to volatile status register */ + switch ((table[0] & (0x01 << 4)) >> 4) { + case 0: + sfdp->vola_sr_we_cmd = SFUD_VOLATILE_SR_WRITE_ENABLE; + SFUD_DEBUG("Flash device requires instruction 50h as the write enable prior " + "to performing a volatile write to the status register."); + break; + case 1: + sfdp->vola_sr_we_cmd = SFUD_CMD_WRITE_ENABLE; + SFUD_DEBUG("Flash device requires instruction 06h as the write enable prior " + "to performing a volatile write to the status register."); + break; + } + break; + } + /* get address bytes, number of bytes used in addressing flash array read, write and erase. */ + switch ((table[2] & (0x03 << 1)) >> 1) { + case 0: + sfdp->addr_3_byte = true; + sfdp->addr_4_byte = false; + SFUD_DEBUG("3-Byte only addressing."); + break; + case 1: + sfdp->addr_3_byte = true; + sfdp->addr_4_byte = true; + SFUD_DEBUG("3- or 4-Byte addressing."); + break; + case 2: + sfdp->addr_3_byte = false; + sfdp->addr_4_byte = true; + SFUD_DEBUG("4-Byte only addressing."); + break; + default: + sfdp->addr_3_byte = false; + sfdp->addr_4_byte = false; + SFUD_INFO("Error: Read address bytes error!"); + return false; + } + /* get flash memory capacity */ + uint32_t table2_temp = (table[7] << 24) | (table[6] << 16) | (table[5] << 8) | table[4]; + switch ((table[7] & (0x01 << 7)) >> 7) { + case 0: + sfdp->capacity = 1 + (table2_temp >> 3); + break; + case 1: + table2_temp &= 0x7FFFFFFF; + if (table2_temp > sizeof(sfdp->capacity) * 8 + 3) { + sfdp->capacity = 0; + SFUD_INFO("Error: The flash capacity is grater than 32 Gb/ 4 GB! Not Supported."); + return false; + } + sfdp->capacity = 1 << (table2_temp - 3); + break; + } + SFUD_DEBUG("Capacity is %ld Bytes.", sfdp->capacity); + /* get erase size and erase command */ + for (i = 0, j = 0; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) { + if (table[28 + 2 * i] != 0x00) { + sfdp->eraser[j].size = 1 << table[28 + 2 * i]; + sfdp->eraser[j].cmd = table[28 + 2 * i + 1]; + SFUD_DEBUG("Flash device supports %ldKB block erase. Command is 0x%02X.", sfdp->eraser[j].size / 1024, + sfdp->eraser[j].cmd); + j++; + } + } + /* sort the eraser size from small to large */ + for (i = 0, j = 0; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) { + if (sfdp->eraser[i].size) { + for (j = i + 1; j < SFUD_SFDP_ERASE_TYPE_MAX_NUM; j++) { + if (sfdp->eraser[j].size != 0 && sfdp->eraser[i].size > sfdp->eraser[j].size) { + /* swap the small eraser */ + uint32_t temp_size = sfdp->eraser[i].size; + uint8_t temp_cmd = sfdp->eraser[i].cmd; + sfdp->eraser[i].size = sfdp->eraser[j].size; + sfdp->eraser[i].cmd = sfdp->eraser[j].cmd; + sfdp->eraser[j].size = temp_size; + sfdp->eraser[j].cmd = temp_cmd; + } + } + } + } + + sfdp->available = true; + return true; +} + +static sfud_err read_sfdp_data(const sfud_flash *flash, uint32_t addr, uint8_t *read_buf, size_t size) { + uint8_t cmd[] = { + SFUD_CMD_READ_SFDP_REGISTER, + (addr >> 16) & 0xFF, + (addr >> 8) & 0xFF, + (addr >> 0) & 0xFF, + SFUD_DUMMY_DATA, + }; + + SFUD_ASSERT(flash); + SFUD_ASSERT(addr < 1 << 24); + SFUD_ASSERT(read_buf); + SFUD_ASSERT(flash->spi.wr); + + return flash->spi.wr(&flash->spi, cmd, sizeof(cmd), read_buf, size); +} + +/** + * get the most suitable eraser for erase process from SFDP parameter + * + * @param flash flash device + * @param addr start address + * @param erase_size will be erased size + * + * @return the eraser index of SFDP eraser table @see sfud_sfdp.eraser[] + */ +size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr, size_t erase_size) { + size_t index = SMALLEST_ERASER_INDEX, i; + /* only used when flash supported SFDP */ + SFUD_ASSERT(flash->sfdp.available); + /* the address isn't align by smallest eraser's size, then use the smallest eraser */ + if (addr % flash->sfdp.eraser[SMALLEST_ERASER_INDEX].size) { + return SMALLEST_ERASER_INDEX; + } + /* Find the suitable eraser. + * The largest size eraser is at the end of eraser table. + * In order to decrease erase command counts, so the find process is from the end of eraser table. */ + for (i = SFUD_SFDP_ERASE_TYPE_MAX_NUM - 1;; i--) { + if ((flash->sfdp.eraser[i].size != 0) && (erase_size >= flash->sfdp.eraser[i].size) + && (addr % flash->sfdp.eraser[i].size == 0)) { + index = i; + break; + } + if (i == SMALLEST_ERASER_INDEX) { + break; + } + } + return index; +} + +#endif /* SFUD_USING_SFDP */ diff --git a/components/drivers/spi/spi_flash_sfud.c b/components/drivers/spi/spi_flash_sfud.c new file mode 100644 index 000000000..c7d7e50c7 --- /dev/null +++ b/components/drivers/spi/spi_flash_sfud.c @@ -0,0 +1,551 @@ +/* + * File : spi_flash_sfud.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2016, RT-Thread Development Team + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2016-09-28 armink first version. + */ + +#include +#include +#include "spi_flash.h" +#include "spi_flash_sfud.h" + +#ifdef RT_USING_SFUD + +#if RT_DEBUG_SFUD +#define SFUD_TRACE rt_kprintf +#else +#define SFUD_TRACE(...) +#endif /* RT_DEBUG_SFUD */ + +#ifndef RT_SFUD_DEFAULT_SPI_CFG +/* read the JEDEC SFDP command must run at 50 MHz or less */ +#define RT_SFUD_DEFAULT_SPI_CFG \ +{ \ + .mode = RT_SPI_MODE_0 | RT_SPI_MSB, \ + .data_width = 8, \ + .max_hz = 50 * 1000 * 1000, \ +} +#endif + +static char log_buf[RT_CONSOLEBUF_SIZE]; + +void sfud_log_debug(const char *file, const long line, const char *format, ...); + +static rt_err_t rt_sfud_control(rt_device_t dev, rt_uint8_t cmd, void *args) { + RT_ASSERT(dev != RT_NULL); + + switch (cmd) { + case RT_DEVICE_CTRL_BLK_GETGEOME: { + struct rt_device_blk_geometry *geometry = (struct rt_device_blk_geometry *) args; + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data); + + if (rtt_dev == RT_NULL || geometry == RT_NULL) { + return -RT_ERROR; + } + + geometry->bytes_per_sector = rtt_dev->geometry.bytes_per_sector; + geometry->sector_count = rtt_dev->geometry.sector_count; + geometry->block_size = rtt_dev->geometry.block_size; + break; + } + case RT_DEVICE_CTRL_BLK_ERASE: { + rt_uint32_t *addrs = (rt_uint32_t *) args, start_addr = addrs[0], end_addr = addrs[1], phy_start_addr; + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data); + sfud_flash *sfud_dev = (sfud_flash *) (rtt_dev->user_data); + rt_size_t phy_size; + + if (addrs == RT_NULL || start_addr > end_addr || rtt_dev == RT_NULL || sfud_dev == RT_NULL) { + return -RT_ERROR; + } + + phy_start_addr = start_addr * rtt_dev->geometry.bytes_per_sector; + phy_size = (end_addr - start_addr) * rtt_dev->geometry.bytes_per_sector; + + if (sfud_erase(sfud_dev, phy_start_addr, phy_size) != SFUD_SUCCESS) { + return -RT_ERROR; + } + break; + } + } + + return RT_EOK; +} + + +static rt_size_t rt_sfud_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data); + sfud_flash *sfud_dev = (sfud_flash *) (rtt_dev->user_data); + /* change the block devices logic address to physical address */ + rt_off_t phy_pos = pos * rtt_dev->geometry.bytes_per_sector; + rt_size_t phy_size = size * rtt_dev->geometry.bytes_per_sector; + + if (sfud_read(sfud_dev, phy_pos, phy_size, buffer) != SFUD_SUCCESS) { + return 0; + } else { + return size; + } +} + +static rt_size_t rt_sfud_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data); + sfud_flash *sfud_dev = (sfud_flash *) (rtt_dev->user_data); + /* change the block devices logic address to physical address */ + rt_off_t phy_pos = pos * rtt_dev->geometry.bytes_per_sector; + rt_size_t phy_size = size * rtt_dev->geometry.bytes_per_sector; + + if (sfud_erase_write(sfud_dev, phy_pos, phy_size, buffer) != SFUD_SUCCESS) { + return 0; + } else { + return size; + } +} + +/** + * SPI write data then read data + */ +static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, + size_t read_size) { + sfud_err result = SFUD_SUCCESS; + sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data); + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data); + + if (write_size) { + RT_ASSERT(write_buf); + } + if (read_size) { + RT_ASSERT(read_buf); + } + + if (write_size && read_size) { + if (rt_spi_send_then_recv(rtt_dev->rt_spi_device, write_buf, write_size, read_buf, read_size) != RT_EOK) { + result = SFUD_ERR_TIMEOUT; + } + } else if (write_size) { + if (rt_spi_send(rtt_dev->rt_spi_device, write_buf, write_size) == 0) { + result = SFUD_ERR_TIMEOUT; + } + } else { + if (rt_spi_recv(rtt_dev->rt_spi_device, read_buf, read_size) == 0) { + result = SFUD_ERR_TIMEOUT; + } + } + + return result; +} + +static void spi_lock(const sfud_spi *spi) { + sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data); + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data); + + rt_mutex_take(&(rtt_dev->lock), RT_WAITING_FOREVER); +} + +static void spi_unlock(const sfud_spi *spi) { + sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data); + struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data); + + rt_mutex_release(&(rtt_dev->lock)); +} + +static void retry_delay_ms(void) { + /* millisecond delay */ + rt_tick_from_millisecond(1); +} + +static void retry_delay_100us(void) { + /* 100 microsecond delay */ + rt_thread_delay((RT_TICK_PER_SECOND * 1 + 9999) / 10000); +} + +/** + * This function is print debug info. + * + * @param file the file which has call this function + * @param line the line number which has call this function + * @param format output format + * @param ... args + */ +void sfud_log_debug(const char *file, const long line, const char *format, ...) { + va_list args; + + /* args point to the first variable parameter */ + va_start(args, format); + rt_kprintf("[SFUD](%s:%ld) ", file, line); + /* must use vprintf to print */ + vsnprintf(log_buf, sizeof(log_buf), format, args); + rt_kprintf("%s\n", log_buf); + va_end(args); +} + +/** + * This function is print routine info. + * + * @param format output format + * @param ... args + */ +void sfud_log_info(const char *format, ...) { + va_list args; + + /* args point to the first variable parameter */ + va_start(args, format); + rt_kprintf("[SFUD]"); + /* must use vprintf to print */ + vsnprintf(log_buf, sizeof(log_buf), format, args); + rt_kprintf("%s\n", log_buf); + va_end(args); +} + +sfud_err sfud_spi_port_init(sfud_flash *flash) { + sfud_err result = SFUD_SUCCESS; + + /* port SPI device interface */ + flash->spi.wr = spi_write_read; + flash->spi.lock = spi_lock; + flash->spi.unlock = spi_unlock; + flash->spi.user_data = flash; + /* 100 microsecond delay */ + flash->retry.delay = retry_delay_100us; + /* 60 seconds timeout */ + flash->retry.times = 60 * 10000; + + + return result; +} + +/** + * SPI Flash device initialize by SFUD(Serial Flash Universal Driver) library + * + * @param rtt_dev the static RT-Thread's flash device object + * @param sfud_dev the static SFUD's flash device object + * + * @return result + */ +rt_err_t rt_sfud_init(struct spi_flash_device *rtt_dev, sfud_flash *sfud_dev) { + extern sfud_err sfud_device_init(sfud_flash *flash); + + RT_ASSERT(rtt_dev); + RT_ASSERT(sfud_dev); + + rt_memset(rtt_dev, 0, sizeof(struct spi_flash_device)); + + /* SPI configure */ + { + /* RT-Thread SPI device initialize */ + rtt_dev->rt_spi_device = (struct rt_spi_device *) rt_device_find(sfud_dev->spi.name); + if (rtt_dev->rt_spi_device == RT_NULL) { + SFUD_TRACE("spi device %s not found!\r\n", sfud_dev->spi.name); + return -RT_ENOSYS; + } + /* using default flash SPI configuration for initialize SPI Flash + * @note you also can change the SPI to other configuration after initialized finish */ + struct rt_spi_configuration cfg = RT_SFUD_DEFAULT_SPI_CFG; + rt_spi_configure(rtt_dev->rt_spi_device, &cfg); + /* initialize lock */ + rt_mutex_init(&(rtt_dev->lock), sfud_dev->name, RT_IPC_FLAG_FIFO); + } + + + /* SFUD flash device initialize */ + { + /* accessed each other */ + rtt_dev->user_data = sfud_dev; + rtt_dev->flash_device.user_data = rtt_dev; + sfud_dev->user_data = rtt_dev; + /* initialize SFUD device */ + if (sfud_device_init(sfud_dev) != SFUD_SUCCESS) { + return -RT_EIO; + } + /* when initialize success, then copy SFUD flash device's geometry to RT-Thread SPI flash device */ + rtt_dev->geometry.sector_count = sfud_dev->chip.capacity / sfud_dev->chip.erase_gran; + rtt_dev->geometry.bytes_per_sector = sfud_dev->chip.erase_gran; + rtt_dev->geometry.block_size = sfud_dev->chip.erase_gran; + } + + /* register device */ + rtt_dev->flash_device.type = RT_Device_Class_Block; + rtt_dev->flash_device.init = RT_NULL; + rtt_dev->flash_device.open = RT_NULL; + rtt_dev->flash_device.close = RT_NULL; + rtt_dev->flash_device.read = rt_sfud_read; + rtt_dev->flash_device.write = rt_sfud_write; + rtt_dev->flash_device.control = rt_sfud_control; + + rt_device_register(&(rtt_dev->flash_device), sfud_dev->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); + + return RT_EOK; +} + +#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) + +#include + +static void sf(uint8_t argc, char **argv) { + +#define CMD_SETECT_INDEX 0 +#define CMD_READ_INDEX 1 +#define CMD_WRITE_INDEX 2 +#define CMD_ERASE_INDEX 3 +#define CMD_RW_STATUS_INDEX 4 +#define CMD_BENCH_INDEX 5 + + sfud_err result = SFUD_SUCCESS; + const static sfud_flash *flash = NULL; + size_t i = 0; + + const char* sf_help_info[] = { + [CMD_SETECT_INDEX] = "sf select [index] - select a flash chip with device's index", + [CMD_READ_INDEX] = "sf read addr size - read 'size' bytes starting at 'addr'", + [CMD_WRITE_INDEX] = "sf write addr data1 ... dataN - write some bytes 'data' to flash starting at 'addr'", + [CMD_ERASE_INDEX] = "sf erase addr size - erase 'size' bytes starting at 'addr'", + [CMD_RW_STATUS_INDEX] = "sf status [ ] - read or write '1:volatile|0:non-volatile' 'status'", + [CMD_BENCH_INDEX] = "sf bench - full chip benchmark. DANGER: it will erase full chip!", + }; + + if (argc < 2) { + rt_kprintf("Usage:\n"); + for (i = 0; i < sizeof(sf_help_info) / sizeof(char*); i++) { + rt_kprintf("%s\n", sf_help_info[i]); + } + rt_kprintf("\n"); + } else { + const char *operator = argv[1]; + uint32_t addr, size; + + if (!strcmp(operator, "select")) { + if (argc < 3) { + rt_kprintf("Usage: %s.\n", sf_help_info[CMD_SETECT_INDEX]); + if (sfud_get_device_num() > 0) { + for (i = 0; i < sfud_get_device_num(); i++) { + if (sfud_get_device(i)->init_ok) { + rt_kprintf("The index %d flash device name is %s, ", i, sfud_get_device(i)->name); + if (sfud_get_device(i)->chip.capacity < 1024 * 1024) { + rt_kprintf("total is %d KB", sfud_get_device(i)->chip.capacity / 1024); + } else { + rt_kprintf("total is %d MB", sfud_get_device(i)->chip.capacity / 1024 / 1024); + } + if (sfud_get_device(i)->chip.name != NULL) { + rt_kprintf(", type is %s", sfud_get_device(i)->chip.name); + } + rt_kprintf(".\n"); + } + } + } else { + rt_kprintf("There is no flash device in device table.\n"); + } + } else { + size_t device_index = atol(argv[2]); + if (device_index >= sfud_get_device_num()) { + rt_kprintf("Flash device index out bound[0:%d].\n", sfud_get_device_num() - 1); + return; + } + if (!sfud_get_device(device_index)->init_ok) { + rt_kprintf("Flash %s isn't initialize OK.\n", sfud_get_device(device_index)->name); + return; + } + flash = sfud_get_device(device_index); + if (flash->chip.capacity < 1024 * 1024) { + rt_kprintf("%d KB %s is current selected device.\n", flash->chip.capacity / 1024, flash->name); + } else { + rt_kprintf("%d MB %s is current selected device.\n", flash->chip.capacity / 1024 / 1024, + flash->name); + } + } + } else { + if (!flash) { + rt_kprintf("No flash device selected. Please run 'sf select'.\n"); + return; + } + if (!rt_strcmp(operator, "read")) { + if (argc < 4) { + rt_kprintf("Usage: %s.\n", sf_help_info[CMD_READ_INDEX]); + return; + } else { + addr = atol(argv[2]); + size = atol(argv[3]); + uint8_t *data = rt_malloc(size); + if (data) { + result = sfud_read(flash, addr, size, data); + if (result == SFUD_SUCCESS) { + rt_kprintf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\n", + flash->name, addr, size); + rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"); + for (i = 0; i < size; i++) { + if (i % 16 == 0) { + rt_kprintf("[%08X] ", addr + i); + } + rt_kprintf("%02X ", data[i]); + if (((i + 1) % 16 == 0) || i == size - 1) { + rt_kprintf("\n"); + } + } + rt_kprintf("\n"); + } + rt_free(data); + } else { + rt_kprintf("Low memory!\n"); + } + } + } else if (!rt_strcmp(operator, "write")) { + if (argc < 4) { + rt_kprintf("Usage: %s.\n", sf_help_info[CMD_WRITE_INDEX]); + return; + } else { + addr = atol(argv[2]); + size = argc - 3; + uint8_t *data = rt_malloc(size); + if (data) { + for (i = 0; i < size; i++) { + data[i] = atoi(argv[3 + i]); + } + result = sfud_write(flash, addr, size, data); + if (result == SFUD_SUCCESS) { + rt_kprintf("Write the %s flash data success. Start from 0x%08X, size is %ld.\n", + flash->name, addr, size); + rt_kprintf("Write data: "); + for (i = 0; i < size; i++) { + rt_kprintf("%d ", data[i]); + } + rt_kprintf(".\n"); + } + rt_free(data); + } else { + rt_kprintf("Low memory!\n"); + } + } + } else if (!rt_strcmp(operator, "erase")) { + if (argc < 4) { + rt_kprintf("Usage: %s.\n", sf_help_info[CMD_ERASE_INDEX]); + return; + } else { + addr = atol(argv[2]); + size = atol(argv[3]); + result = sfud_erase(flash, addr, size); + if (result == SFUD_SUCCESS) { + rt_kprintf("Erase the %s flash data success. Start from 0x%08X, size is %ld.\n", flash->name, + addr, size); + } + } + } else if (!rt_strcmp(operator, "status")) { + if (argc < 3) { + uint8_t status; + result = sfud_read_status(flash, &status); + if (result == SFUD_SUCCESS) { + rt_kprintf("The %s flash status register current value is 0x%02X.\n", flash->name, status); + } + } else if (argc == 4) { + bool is_volatile = atoi(argv[2]); + uint8_t status = atoi(argv[3]); + result = sfud_write_status(flash, is_volatile, status); + if (result == SFUD_SUCCESS) { + rt_kprintf("Write the %s flash status register to 0x%02X success.\n", flash->name, status); + } + } else { + rt_kprintf("Usage: %s.\n", sf_help_info[CMD_RW_STATUS_INDEX]); + return; + } + } else if (!rt_strcmp(operator, "bench")) { + /* full chip benchmark test */ + addr = 0; + size = flash->chip.capacity; + uint32_t start_time, time_cast; + rt_uint32_t total_mem, used_mem, max_uesd_mem; + rt_memory_info(&total_mem, &used_mem, &max_uesd_mem); + size_t write_size = SFUD_WRITE_MAX_PAGE_SIZE, read_size; + if ((total_mem - used_mem) / 2 < size) { + read_size = (total_mem - used_mem) / 2; + } else { + read_size = size; + } + uint8_t *write_data = rt_malloc(write_size), *read_data = rt_malloc(read_size); + + if (write_data && read_data) { + rt_memset(write_data, 0x55, write_size); + /* benchmark testing */ + rt_kprintf("Erasing the %s %ld bytes data, waiting...\n", flash->name, size); + start_time = rt_tick_get(); + result = sfud_erase(flash, addr, size); + if (result == SFUD_SUCCESS) { + time_cast = rt_tick_get() - start_time; + rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND, + time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000)); + } else { + rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result); + } + /* write test */ + rt_kprintf("Writing the %s %ld bytes data, waiting...\n", flash->name, size); + start_time = rt_tick_get(); + for (i = 0; i < size; i += write_size) { + result = sfud_write(flash, addr + i, write_size, write_data); + if (result != SFUD_SUCCESS) { + break; + } + } + if (result == SFUD_SUCCESS) { + time_cast = rt_tick_get() - start_time; + rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND, + time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000)); + } else { + rt_kprintf("Write benchmark has an error. Error code: %d.\n", result); + } + /* read test */ + rt_kprintf("Reading the %s %ld bytes data, waiting...\n", flash->name, size); + start_time = rt_tick_get(); + for (i = 0; i < size; i += read_size) { + if (i + read_size <= size) { + result = sfud_read(flash, addr + i, read_size, read_data); + } else { + result = sfud_read(flash, addr + i, size - i, read_data); + } + if (result != SFUD_SUCCESS) { + break; + } + } + if (result == SFUD_SUCCESS) { + time_cast = rt_tick_get() - start_time; + rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND, + time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000)); + } else { + rt_kprintf("Read benchmark has an error. Error code: %d.\n", result); + } + } else { + rt_kprintf("Low memory!\n"); + } + rt_free(write_data); + rt_free(read_data); + } else { + rt_kprintf("Usage:\n"); + for (i = 0; i < sizeof(sf_help_info) / sizeof(char*); i++) { + rt_kprintf("%s\n", sf_help_info[i]); + } + rt_kprintf("\n"); + return; + } + if (result != SFUD_SUCCESS) { + rt_kprintf("This flash operate has an error. Error code: %d.\n", result); + } + } + } +} +MSH_CMD_EXPORT(sf, SPI Flash operate.); + +#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */ + +#endif /* RT_USING_SFUD */ diff --git a/components/drivers/spi/spi_flash_sfud.h b/components/drivers/spi/spi_flash_sfud.h new file mode 100644 index 000000000..7fdab6f60 --- /dev/null +++ b/components/drivers/spi/spi_flash_sfud.h @@ -0,0 +1,41 @@ +/* + * File : spi_flash_sfud.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2016, RT-Thread Development Team + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2016-09-28 armink first version. + */ + +#ifndef _SPI_FLASH_SFUD_H_ +#define _SPI_FLASH_SFUD_H_ + +#include +#include "./sfud/inc/sfud.h" + +/** + * SPI Flash device initialize by SFUD(Serial Flash Universal Driver) library + * + * @param rtt_dev the static RT-Thread's flash device object + * @param sfud_dev the static SFUD's flash device object + * + * @return result + */ +extern rt_err_t rt_sfud_init(struct spi_flash_device *rtt_flash, sfud_flash *sfud_flash); + +#endif /* _SPI_FLASH_SFUD_H_ */