mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-28 01:23:34 +08:00
506 lines
17 KiB
C
506 lines
17 KiB
C
/****************************************************************************
|
|
*
|
|
* Copyright (C) 2020 PX4 Development Team. All rights reserved.
|
|
*
|
|
* 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 PX4 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* @file flash_w25q128.c
|
|
*
|
|
* Board-specific external flash W25Q128 functions.
|
|
*/
|
|
|
|
|
|
#include "board_config.h"
|
|
#include "qspi.h"
|
|
#include "arm_internal.h"
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
|
|
|
|
/************************************************************************************
|
|
* Pre-processor Definitions
|
|
************************************************************************************/
|
|
/* Configuration ********************************************************************/
|
|
|
|
#define N25Q128_SECTOR_SIZE (4*1024)
|
|
#define N25Q128_SECTOR_SHIFT (12)
|
|
#define N25Q128_SECTOR_COUNT (4096)
|
|
#define N25Q128_PAGE_SIZE (256)
|
|
#define N25Q128_PAGE_SHIFT (8)
|
|
|
|
#define W25Q_DUMMY_CYCLES_FAST_READ_QUAD 6
|
|
#define W25Q_INSTR_FAST_READ_QUAD 0xEB
|
|
#define W25Q_ADDRESS_SIZE 3 // 3 bytes -> 24 bits
|
|
|
|
#define N25QXXX_READ_STATUS 0x05 /* Read status register: *
|
|
* 0x05 | SR */
|
|
#define N25QXXX_PAGE_PROGRAM 0x02 /* Page Program: *
|
|
* 0x02 | ADDR(MS) | ADDR(MID) | *
|
|
* ADDR(LS) | data */
|
|
#define N25QXXX_WRITE_ENABLE 0x06 /* Write enable: *
|
|
* 0x06 */
|
|
#define N25QXXX_WRITE_DISABLE 0x04 /* Write disable command code: *
|
|
* 0x04 */
|
|
#define N25QXXX_SUBSECTOR_ERASE 0x20 /* Sub-sector Erase (4 kB) *
|
|
* 0x20 | ADDR(MS) | ADDR(MID) | *
|
|
* ADDR(LS) */
|
|
|
|
|
|
/* N25QXXX Registers ****************************************************************/
|
|
/* Status register bit definitions */
|
|
|
|
#define STATUS_BUSY_MASK (1 << 0) /* Bit 0: Device ready/busy status */
|
|
# define STATUS_READY (0 << 0) /* 0 = Not Busy */
|
|
# define STATUS_BUSY (1 << 0) /* 1 = Busy */
|
|
#define STATUS_WEL_MASK (1 << 1) /* Bit 1: Write enable latch status */
|
|
# define STATUS_WEL_DISABLED (0 << 1) /* 0 = Not Write Enabled */
|
|
# define STATUS_WEL_ENABLED (1 << 1) /* 1 = Write Enabled */
|
|
#define STATUS_BP_SHIFT (2) /* Bits 2-4: Block protect bits */
|
|
#define STATUS_BP_MASK (7 << STATUS_BP_SHIFT)
|
|
# define STATUS_BP_NONE (0 << STATUS_BP_SHIFT)
|
|
# define STATUS_BP_ALL (7 << STATUS_BP_SHIFT)
|
|
#define STATUS_TB_MASK (1 << 5) /* Bit 5: Top / Bottom Protect */
|
|
# define STATUS_TB_TOP (0 << 5) /* 0 = BP2-BP0 protect Top down */
|
|
# define STATUS_TB_BOTTOM (1 << 5) /* 1 = BP2-BP0 protect Bottom up */
|
|
#define STATUS_BP3_MASK (1 << 5) /* Bit 6: BP3 */
|
|
#define STATUS_SRP0_MASK (1 << 7) /* Bit 7: Status register protect 0 */
|
|
# define STATUS_SRP0_UNLOCKED (0 << 7) /* 0 = WP# no effect / PS Lock Down */
|
|
# define STATUS_SRP0_LOCKED (1 << 7) /* 1 = WP# protect / OTP Lock Down */
|
|
|
|
/************************************************************************************
|
|
* 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
|
|
* pointers to struct mtd_dev_s and struct n25qxxx_dev_s.
|
|
*/
|
|
|
|
struct n25qxxx_dev_s {
|
|
//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 */
|
|
FAR uint8_t *readbuf; /* Allocated status read buffer */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
struct qspi_dev_s *ptr_qspi_dev;
|
|
struct qspi_meminfo_s qspi_meminfo = {
|
|
.flags = QSPIMEM_QUADIO,
|
|
.addrlen = W25Q_ADDRESS_SIZE,
|
|
.dummies = W25Q_DUMMY_CYCLES_FAST_READ_QUAD,
|
|
.cmd = W25Q_INSTR_FAST_READ_QUAD
|
|
};
|
|
|
|
struct n25qxxx_dev_s n25qxxx_dev;
|
|
uint8_t cmdbuf[4] = {0u};
|
|
uint8_t readbuf[1] = {0u};
|
|
|
|
/************************************************************************************
|
|
* Private Functions
|
|
************************************************************************************/
|
|
__ramfunc__ int n25qxxx_command(FAR struct qspi_dev_s *qspi, uint8_t cmd);
|
|
__ramfunc__ uint8_t n25qxxx_read_status(FAR struct n25qxxx_dev_s *priv);
|
|
__ramfunc__ int n25qxxx_command_read(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
FAR void *buffer, size_t buflen);
|
|
__ramfunc__ void n25qxxx_write_enable(FAR struct n25qxxx_dev_s *priv);
|
|
__ramfunc__ void n25qxxx_write_disable(FAR struct n25qxxx_dev_s *priv);
|
|
|
|
__ramfunc__ int n25qxxx_write_page(struct n25qxxx_dev_s *priv, FAR const uint8_t *buffer,
|
|
off_t address, size_t buflen);
|
|
|
|
__ramfunc__ int n25qxxx_write_one_page(struct n25qxxx_dev_s *priv, struct qspi_meminfo_s *meminfo);
|
|
|
|
__ramfunc__ int n25qxxx_erase_sector(struct n25qxxx_dev_s *priv, off_t sector);
|
|
|
|
__ramfunc__ bool n25qxxx_isprotected(FAR struct n25qxxx_dev_s *priv, uint8_t status,
|
|
off_t address);
|
|
|
|
__ramfunc__ int n25qxxx_command_address(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
off_t addr, uint8_t addrlen);
|
|
|
|
/************************************************************************************
|
|
* Public Functions
|
|
************************************************************************************/
|
|
|
|
void flash_w25q128_init(void)
|
|
{
|
|
int qspi_interface_number = 0;
|
|
ptr_qspi_dev = stm32h7_qspi_initialize(qspi_interface_number);
|
|
n25qxxx_dev.qspi = ptr_qspi_dev;
|
|
n25qxxx_dev.cmdbuf = cmdbuf;
|
|
n25qxxx_dev.readbuf = readbuf;
|
|
n25qxxx_dev.sectorshift = N25Q128_SECTOR_SHIFT;
|
|
n25qxxx_dev.pageshift = N25Q128_PAGE_SHIFT;
|
|
n25qxxx_dev.nsectors = N25Q128_SECTOR_COUNT;
|
|
}
|
|
|
|
__ramfunc__ ssize_t up_progmem_ext_getpage(size_t addr)
|
|
{
|
|
ssize_t page_address = (addr - STM32_FMC_BANK4) / N25Q128_SECTOR_COUNT;
|
|
|
|
return page_address;
|
|
}
|
|
|
|
__ramfunc__ ssize_t up_progmem_ext_eraseblock(size_t block)
|
|
{
|
|
ssize_t size = N25Q128_SECTOR_COUNT;
|
|
|
|
irqstate_t irqstate = px4_enter_critical_section();
|
|
stm32h7_qspi_exit_memorymapped(ptr_qspi_dev);
|
|
|
|
n25qxxx_erase_sector(&n25qxxx_dev, block);
|
|
|
|
stm32h7_qspi_enter_memorymapped(ptr_qspi_dev, &qspi_meminfo, 0);
|
|
px4_leave_critical_section(irqstate);
|
|
return size;
|
|
}
|
|
|
|
__ramfunc__ ssize_t up_progmem_ext_write(size_t addr, FAR const void *buf, size_t count)
|
|
{
|
|
ssize_t ret_val = 0;
|
|
|
|
irqstate_t irqstate = px4_enter_critical_section();
|
|
stm32h7_qspi_exit_memorymapped(ptr_qspi_dev);
|
|
|
|
addr &= 0xFFFFFF;
|
|
n25qxxx_write_page(&n25qxxx_dev, buf, (off_t)addr, count);
|
|
|
|
stm32h7_qspi_enter_memorymapped(ptr_qspi_dev, &qspi_meminfo, 0);
|
|
px4_leave_critical_section(irqstate);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_command
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ int n25qxxx_command(FAR struct qspi_dev_s *qspi, uint8_t cmd)
|
|
{
|
|
struct qspi_cmdinfo_s cmdinfo;
|
|
|
|
finfo("CMD: %02" PRIx8 "\n", cmd);
|
|
|
|
cmdinfo.flags = 0;
|
|
cmdinfo.addrlen = 0;
|
|
cmdinfo.cmd = cmd;
|
|
cmdinfo.buflen = 0;
|
|
cmdinfo.addr = 0;
|
|
cmdinfo.buffer = NULL;
|
|
|
|
int rv;
|
|
rv = qspi_command(qspi, &cmdinfo);
|
|
return rv;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_read_status
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ uint8_t n25qxxx_read_status(FAR struct n25qxxx_dev_s *priv)
|
|
{
|
|
DEBUGVERIFY(n25qxxx_command_read(priv->qspi, N25QXXX_READ_STATUS,
|
|
(FAR void *)&priv->readbuf[0], 1));
|
|
return priv->readbuf[0];
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_command_read
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ int n25qxxx_command_read(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
FAR void *buffer, size_t buflen)
|
|
{
|
|
struct qspi_cmdinfo_s cmdinfo;
|
|
|
|
finfo("CMD: %02" PRIx8 " buflen: %zu\n", cmd, buflen);
|
|
|
|
cmdinfo.flags = QSPICMD_READDATA;
|
|
cmdinfo.addrlen = 0;
|
|
cmdinfo.cmd = cmd;
|
|
cmdinfo.buflen = buflen;
|
|
cmdinfo.addr = 0;
|
|
cmdinfo.buffer = buffer;
|
|
|
|
int rv;
|
|
rv = qspi_command(qspi, &cmdinfo);
|
|
return rv;
|
|
}
|
|
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_write_enable
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ void n25qxxx_write_enable(FAR struct n25qxxx_dev_s *priv)
|
|
{
|
|
uint8_t status;
|
|
|
|
do {
|
|
n25qxxx_command(priv->qspi, N25QXXX_WRITE_ENABLE);
|
|
status = n25qxxx_read_status(priv);
|
|
} while ((status & STATUS_WEL_MASK) != STATUS_WEL_ENABLED);
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_write_disable
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ void n25qxxx_write_disable(FAR struct n25qxxx_dev_s *priv)
|
|
{
|
|
uint8_t status;
|
|
|
|
do {
|
|
n25qxxx_command(priv->qspi, N25QXXX_WRITE_DISABLE);
|
|
status = n25qxxx_read_status(priv);
|
|
} while ((status & STATUS_WEL_MASK) != STATUS_WEL_DISABLED);
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_write_page
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ int n25qxxx_write_page(struct n25qxxx_dev_s *priv, FAR const uint8_t *buffer,
|
|
off_t address, size_t buflen)
|
|
{
|
|
struct qspi_meminfo_s meminfo;
|
|
unsigned int pagesize;
|
|
unsigned int npages;
|
|
unsigned int firstpagesize = 0;
|
|
int ret = OK;
|
|
unsigned int i;
|
|
|
|
finfo("address: %08jx buflen: %zu\n", (intmax_t)address, buflen);
|
|
|
|
pagesize = (1 << priv->pageshift);
|
|
|
|
/* Set up non-varying parts of transfer description */
|
|
|
|
meminfo.flags = QSPIMEM_WRITE;
|
|
meminfo.cmd = N25QXXX_PAGE_PROGRAM;
|
|
meminfo.addrlen = 3;
|
|
meminfo.dummies = 0;
|
|
meminfo.buffer = (void *)buffer;
|
|
|
|
if (0 != (address % pagesize)) {
|
|
firstpagesize = pagesize - (address % pagesize);
|
|
}
|
|
|
|
if (buflen <= firstpagesize) {
|
|
meminfo.addr = address;
|
|
meminfo.buflen = buflen;
|
|
ret = n25qxxx_write_one_page(priv, &meminfo);
|
|
|
|
} else {
|
|
|
|
if (firstpagesize > 0) {
|
|
meminfo.addr = address;
|
|
meminfo.buflen = firstpagesize;
|
|
ret = n25qxxx_write_one_page(priv, &meminfo);
|
|
|
|
buffer += firstpagesize;
|
|
address += firstpagesize;
|
|
buflen -= firstpagesize;
|
|
}
|
|
|
|
npages = (buflen >> priv->pageshift);
|
|
|
|
meminfo.buflen = pagesize;
|
|
|
|
/* Then write each page */
|
|
|
|
for (i = 0; (i < npages) && (ret == OK); i++) {
|
|
/* Set up varying parts of the transfer description */
|
|
|
|
meminfo.addr = address;
|
|
meminfo.buffer = (void *)buffer;
|
|
|
|
ret = n25qxxx_write_one_page(priv, &meminfo);
|
|
|
|
/* Update for the next time through the loop */
|
|
|
|
buffer += pagesize;
|
|
address += pagesize;
|
|
buflen -= pagesize;
|
|
}
|
|
|
|
if ((ret == OK) && (buflen > 0)) {
|
|
meminfo.addr = address;
|
|
meminfo.buffer = (void *)buffer;
|
|
meminfo.buflen = buflen;
|
|
|
|
ret = n25qxxx_write_one_page(priv, &meminfo);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
__ramfunc__ int n25qxxx_write_one_page(struct n25qxxx_dev_s *priv, struct qspi_meminfo_s *meminfo)
|
|
{
|
|
int ret;
|
|
|
|
n25qxxx_write_enable(priv);
|
|
ret = qspi_memory(priv->qspi, meminfo);
|
|
n25qxxx_write_disable(priv);
|
|
|
|
if (ret < 0) {
|
|
ferr("ERROR: QSPI_MEMORY failed writing address=%06" PRIx32 "\n",
|
|
meminfo->addr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_erase_sector
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ int n25qxxx_erase_sector(struct n25qxxx_dev_s *priv, off_t sector)
|
|
{
|
|
off_t address;
|
|
uint8_t status;
|
|
|
|
finfo("sector: %08jx\n", (intmax_t) sector);
|
|
|
|
/* Check that the flash is ready and unprotected */
|
|
|
|
status = n25qxxx_read_status(priv);
|
|
|
|
if ((status & STATUS_BUSY_MASK) != STATUS_READY) {
|
|
ferr("ERROR: Flash busy: %02" PRIx8, status);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Get the address associated with the sector */
|
|
|
|
address = (off_t)sector << priv->sectorshift;
|
|
|
|
if ((status & (STATUS_BP3_MASK | STATUS_BP_MASK)) != 0 &&
|
|
n25qxxx_isprotected(priv, status, address)) {
|
|
ferr("ERROR: Flash protected: %02" PRIx8, status);
|
|
return -EACCES;
|
|
}
|
|
|
|
/* Send the sector erase command */
|
|
|
|
n25qxxx_write_enable(priv);
|
|
n25qxxx_command_address(priv->qspi, N25QXXX_SUBSECTOR_ERASE, address, 3);
|
|
|
|
/* Wait for erasure to finish */
|
|
|
|
while ((n25qxxx_read_status(priv) & STATUS_BUSY_MASK) != 0);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_isprotected
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ bool n25qxxx_isprotected(FAR struct n25qxxx_dev_s *priv, uint8_t status,
|
|
off_t address)
|
|
{
|
|
off_t protstart;
|
|
off_t protend;
|
|
off_t protsize;
|
|
unsigned int bp;
|
|
|
|
/* The BP field is spread across non-contiguous bits */
|
|
|
|
bp = (status & STATUS_BP_MASK) >> STATUS_BP_SHIFT;
|
|
|
|
if (status & STATUS_BP3_MASK) {
|
|
bp |= 8;
|
|
}
|
|
|
|
/* the BP field is essentially the power-of-two of the number of 64k sectors,
|
|
* saturated to the device size.
|
|
*/
|
|
|
|
if (0 == bp) {
|
|
return false;
|
|
}
|
|
|
|
protsize = 0x00010000;
|
|
protsize <<= (protsize << (bp - 1));
|
|
protend = (1 << priv->sectorshift) * priv->nsectors;
|
|
|
|
if (protsize > protend) {
|
|
protsize = protend;
|
|
}
|
|
|
|
/* The final protection range then depends on if the protection region is
|
|
* configured top-down or bottom up (assuming CMP=0).
|
|
*/
|
|
|
|
if ((status & STATUS_TB_MASK) != 0) {
|
|
protstart = 0x00000000;
|
|
protend = protstart + protsize;
|
|
|
|
} else {
|
|
protstart = protend - protsize;
|
|
/* protend already computed above */
|
|
}
|
|
|
|
return (address >= protstart && address < protend);
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Name: n25qxxx_command_address
|
|
************************************************************************************/
|
|
|
|
__ramfunc__ int n25qxxx_command_address(FAR struct qspi_dev_s *qspi, uint8_t cmd,
|
|
off_t addr, uint8_t addrlen)
|
|
{
|
|
struct qspi_cmdinfo_s cmdinfo;
|
|
|
|
finfo("CMD: %02" PRIx8 " Address: %04jx addrlen=%" PRIx8 "\n", cmd, (intmax_t) addr, addrlen);
|
|
|
|
cmdinfo.flags = QSPICMD_ADDRESS;
|
|
cmdinfo.addrlen = addrlen;
|
|
cmdinfo.cmd = cmd;
|
|
cmdinfo.buflen = 0;
|
|
cmdinfo.addr = addr;
|
|
cmdinfo.buffer = NULL;
|
|
|
|
int rv;
|
|
rv = qspi_command(qspi, &cmdinfo);
|
|
return rv;
|
|
}
|