riscv: Add first cut of Flash driver for Freedom E platforms. Completely untested.
This commit is contained in:
parent
e51b0360f5
commit
05b12c5c20
|
@ -22,6 +22,7 @@ NOR_DRIVERS = \
|
|||
dsp5680xx_flash.c \
|
||||
efm32.c \
|
||||
em357.c \
|
||||
fespi.c \
|
||||
faux.c \
|
||||
fm3.c \
|
||||
fm4.c \
|
||||
|
|
|
@ -37,6 +37,7 @@ extern struct flash_driver em357_flash;
|
|||
extern struct flash_driver faux_flash;
|
||||
extern struct flash_driver fm3_flash;
|
||||
extern struct flash_driver fm4_flash;
|
||||
extern struct flash_driver fespi_flash;
|
||||
extern struct flash_driver jtagspi_flash;
|
||||
extern struct flash_driver kinetis_flash;
|
||||
extern struct flash_driver kinetis_ke_flash;
|
||||
|
@ -89,6 +90,7 @@ static struct flash_driver *flash_drivers[] = {
|
|||
&faux_flash,
|
||||
&fm3_flash,
|
||||
&fm4_flash,
|
||||
&fespi_flash,
|
||||
&jtagspi_flash,
|
||||
&kinetis_flash,
|
||||
&kinetis_ke_flash,
|
||||
|
|
|
@ -0,0 +1,627 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
|
||||
* Modified by Megan Wachs <megan@sifive.com> from the original stmsmi.c *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
/* The Freedeom E SPI controller is a SPI bus controller
|
||||
* specifically designed for SPI Flash Memories on Freedom E platforms.
|
||||
*
|
||||
* Two working modes are available:
|
||||
* - SW mode: the SPI is controlled by SW. Any custom commands can be sent
|
||||
* on the bus. Writes are only possible in this mode.
|
||||
* - HW mode: Memory content is directly
|
||||
* accessible in CPU memory space. CPU can read, write and execute memory
|
||||
* content. */
|
||||
|
||||
/* ATTENTION:
|
||||
* To have flash memory mapped in CPU memory space, the controller
|
||||
* has have "HW mode" enabled.
|
||||
* 1) The command "reset init" has to initialize the controller and put
|
||||
* it in HW mode (this is actually the default out of reset for Freedom E systems).
|
||||
* 2) every command in this file have to return to prompt in HW mode. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include "spi.h"
|
||||
#include <jtag/jtag.h>
|
||||
#include <helper/time_support.h>
|
||||
|
||||
/* Register offsets */
|
||||
|
||||
#define FESPI_REG_SCKDIV 0x00
|
||||
#define FESPI_REG_SCKMODE 0x04
|
||||
#define FESPI_REG_CSID 0x10
|
||||
#define FESPI_REG_CSDEF 0x14
|
||||
#define FESPI_REG_CSMODE 0x18
|
||||
|
||||
#define FESPI_REG_DCSSCK 0x28
|
||||
#define FESPI_REG_DSCKCS 0x2a
|
||||
#define FESPI_REG_DINTERCS 0x2c
|
||||
#define FESPI_REG_DINTERXFR 0x2e
|
||||
|
||||
#define FESPI_REG_FMT 0x40
|
||||
#define FESPI_REG_TXFIFO 0x48
|
||||
#define FESPI_REG_RXFIFO 0x4c
|
||||
#define FESPI_REG_TXCTRL 0x50
|
||||
#define FESPI_REG_RXCTRL 0x54
|
||||
|
||||
#define FESPI_REG_FCTRL 0x60
|
||||
#define FESPI_REG_FFMT 0x64
|
||||
|
||||
#define FESPI_REG_IE 0x70
|
||||
#define FESPI_REG_IP 0x74
|
||||
|
||||
/* Fields */
|
||||
|
||||
#define FESPI_SCK_POL 0x1
|
||||
#define FESPI_SCK_PHA 0x2
|
||||
|
||||
#define FESPI_FMT_PROTO(x) ((x) & 0x3)
|
||||
#define FESPI_FMT_ENDIAN(x) (((x) & 0x1) << 2)
|
||||
#define FESPI_FMT_DIR(x) (((x) & 0x1) << 3)
|
||||
#define FESPI_FMT_LEN(x) (((x) & 0xf) << 16)
|
||||
|
||||
/* TXCTRL register */
|
||||
#define FESPI_TXWM(x) ((x) & 0xffff)
|
||||
/* RXCTRL register */
|
||||
#define FESPI_RXWM(x) ((x) & 0xffff)
|
||||
|
||||
#define FESPI_IP_TXWM 0x1
|
||||
#define FESPI_IP_RXWM 0x2
|
||||
|
||||
#define FESPI_FCTRL_EN 0x1
|
||||
|
||||
#define FESPI_INSN_CMD_EN 0x1
|
||||
#define FESPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1)
|
||||
#define FESPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4)
|
||||
#define FESPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8)
|
||||
#define FESPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10)
|
||||
#define FESPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12)
|
||||
#define FESPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16)
|
||||
#define FESPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24)
|
||||
|
||||
/* Values */
|
||||
|
||||
#define FESPI_CSMODE_AUTO 0
|
||||
#define FESPI_CSMODE_HOLD 2
|
||||
#define FESPI_CSMODE_OFF 3
|
||||
|
||||
#define FESPI_DIR_RX 0
|
||||
#define FESPI_DIR_TX 1
|
||||
|
||||
#define FESPI_PROTO_S 0
|
||||
#define FESPI_PROTO_D 1
|
||||
#define FESPI_PROTO_Q 2
|
||||
|
||||
#define FESPI_ENDIAN_MSB 0
|
||||
#define FESPI_ENDIAN_LSB 1
|
||||
|
||||
|
||||
/* Timeout in ms */
|
||||
#define FESPI_CMD_TIMEOUT (100)
|
||||
#define FESPI_PROBE_TIMEOUT (100)
|
||||
#define FESPI_MAX_TIMEOUT (3000)
|
||||
|
||||
|
||||
#define FESPI_READ_REG(a) (_FESPI_READ_REG(a))
|
||||
#define _FESPI_READ_REG(a) \
|
||||
{ \
|
||||
int __a; \
|
||||
uint32_t __v; \
|
||||
\
|
||||
__a = target_read_u32(target, ctrl_base + (a), &__v); \
|
||||
if (__a != ERROR_OK) \
|
||||
return __a; \
|
||||
__v; \
|
||||
}
|
||||
|
||||
#define FESPI_WRITE_REG(a, v) \
|
||||
{ \
|
||||
int __r; \
|
||||
\
|
||||
__r = target_write_u32(target, ctrl_base + (a), (v)); \
|
||||
if (__r != ERROR_OK) \
|
||||
return __r; \
|
||||
}
|
||||
|
||||
#define FESPI_DISABLE_HW_MODE() FESPI_WRITE_REG(FESPI_REG_FCTRL, \
|
||||
FESPI_READ_REG(FESPI_REG_FCTRL) & ~FESPI_FCTRL_EN)
|
||||
#define FESPI_ENABLE_HW_MODE() FESPI_WRITE_REG(FESPI_REG_FCTRL, \
|
||||
FESPI_READ_REG(FESPI_REG_FCTRL) | FESPI_FCTRL_EN)
|
||||
|
||||
struct fespi_flash_bank {
|
||||
int probed;
|
||||
uint32_t ctrl_base;
|
||||
const struct flash_device *dev;
|
||||
};
|
||||
|
||||
struct fespi_target {
|
||||
char *name;
|
||||
uint32_t tap_idcode;
|
||||
uint32_t ctrl_base;
|
||||
};
|
||||
|
||||
//TODO !!! What is the right naming convention here?
|
||||
static const struct fespi_target target_devices[] = {
|
||||
/* name, tap_idcode, ctrl_base */
|
||||
{ "Freedom E300 SPI Flash", 0x10e31913 , 0x10014000 },
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command)
|
||||
{
|
||||
struct fespi_flash_bank *fespi_info;
|
||||
|
||||
LOG_DEBUG("%s", __func__);
|
||||
|
||||
if (CMD_ARGC < 6)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
fespi_info = malloc(sizeof(struct fespi_flash_bank));
|
||||
if (fespi_info == NULL) {
|
||||
LOG_ERROR("not enough memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
bank->driver_priv = fespi_info;
|
||||
fespi_info->probed = 0;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
//!!! TODO: Clean up the use of this function.
|
||||
// Should just change the direction for this file's use.
|
||||
static int fespi_fmt (struct flash_bank * bank, bool dir) {
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
|
||||
FESPI_WRITE_REG(FESPI_REG_FMT,
|
||||
FESPI_FMT_PROTO(FESPI_PROTO_S) |
|
||||
FESPI_FMT_PROTO(FESPI_ENDIAN_MSB) |
|
||||
FESPI_FMT_DIR(dir) |
|
||||
FESPI_FMT_LEN(8));
|
||||
|
||||
return ERROR_OK;
|
||||
|
||||
}
|
||||
|
||||
static int fespi_flush(struct flash_bank *bank) {
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
|
||||
while (!(FESPI_READ_REG(FESPI_REG_IP) & FESPI_IP_TXWM));
|
||||
|
||||
return ERROR_OK;
|
||||
|
||||
}
|
||||
|
||||
static int fespi_tx(struct flash_bank *bank, uint8_t in){
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
|
||||
while ((int32_t) FESPI_READ_REG(FESPI_REG_TXFIFO) < 0);
|
||||
FESPI_WRITE_REG(FESPI_REG_TXFIFO, in);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static uint8_t fespi_rx(struct flash_bank * bank) {
|
||||
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
|
||||
int32_t out;
|
||||
while ((out = (int32_t) FESPI_READ_REG(FESPI_REG_RXFIFO)) < 0);
|
||||
|
||||
return out & 0xFF;
|
||||
|
||||
}
|
||||
|
||||
//TODO!!! Why don't we need to call this after writing?
|
||||
static int fespi_wip (struct flash_bank * bank, int timeout) {
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
|
||||
int64_t endtime;
|
||||
|
||||
|
||||
fespi_fmt(bank, FESPI_DIR_RX);
|
||||
|
||||
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
|
||||
endtime = timeval_ms() + timeout;
|
||||
|
||||
fespi_tx(bank, SPIFLASH_READ_STATUS);
|
||||
fespi_rx(bank);
|
||||
|
||||
do {
|
||||
|
||||
fespi_tx(bank, 0);
|
||||
if ((fespi_rx(bank) & SPIFLASH_BSY_BIT) == 0) {
|
||||
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
|
||||
fespi_fmt(bank, FESPI_DIR_TX);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
alive_sleep(1);
|
||||
|
||||
} while (timeval_ms() < endtime);
|
||||
|
||||
LOG_ERROR("timeout");
|
||||
return ERROR_FAIL;
|
||||
|
||||
}
|
||||
|
||||
static int fespi_erase_sector(struct flash_bank *bank, int sector)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
int retval;
|
||||
|
||||
fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
|
||||
fespi_flush(bank);
|
||||
|
||||
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
|
||||
fespi_tx(bank, fespi_info->dev->erase_cmd);
|
||||
sector = bank->sectors[sector].offset;
|
||||
fespi_tx(bank, sector >> 16);
|
||||
fespi_tx(bank, sector >> 8);
|
||||
fespi_tx(bank, sector);
|
||||
fespi_flush(bank);
|
||||
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
|
||||
|
||||
retval = fespi_wip(bank, FESPI_MAX_TIMEOUT);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int fespi_erase(struct flash_bank *bank, int first, int last)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
int retval = ERROR_OK;
|
||||
int sector;
|
||||
|
||||
LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
|
||||
LOG_ERROR("Flash sector invalid");
|
||||
return ERROR_FLASH_SECTOR_INVALID;
|
||||
}
|
||||
|
||||
if (!(fespi_info->probed)) {
|
||||
LOG_ERROR("Flash bank not probed");
|
||||
return ERROR_FLASH_BANK_NOT_PROBED;
|
||||
}
|
||||
|
||||
for (sector = first; sector <= last; sector++) {
|
||||
if (bank->sectors[sector].is_protected) {
|
||||
LOG_ERROR("Flash sector %d protected", sector);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
for (sector = first; sector <= last; sector++) {
|
||||
retval = fespi_erase_sector(bank, sector);
|
||||
if (retval != ERROR_OK)
|
||||
break;
|
||||
keep_alive();
|
||||
}
|
||||
|
||||
/* Switch to HW mode before return to prompt */
|
||||
FESPI_ENABLE_HW_MODE();
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int fespi_protect(struct flash_bank *bank, int set,
|
||||
int first, int last)
|
||||
{
|
||||
int sector;
|
||||
|
||||
for (sector = first; sector <= last; sector++)
|
||||
bank->sectors[sector].is_protected = set;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* This should write something less than or equal to a page.*/
|
||||
|
||||
static int fespi_write_buffer(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t len)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
|
||||
uint32_t ii;
|
||||
|
||||
//TODO!!! assert that len < page size
|
||||
|
||||
LOG_DEBUG("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32,
|
||||
__func__, offset, len);
|
||||
|
||||
fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
|
||||
fespi_flush(bank);
|
||||
|
||||
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
|
||||
|
||||
fespi_tx(bank, SPIFLASH_PAGE_PROGRAM);
|
||||
|
||||
fespi_tx(bank, offset >> 16);
|
||||
fespi_tx(bank, offset >> 8);
|
||||
fespi_tx(bank, offset);
|
||||
|
||||
for (ii = 0; ii < len; ii++) {
|
||||
fespi_tx(bank, buffer[ii]);
|
||||
}
|
||||
|
||||
fespi_flush(bank);
|
||||
|
||||
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int fespi_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
uint32_t cur_count, page_size, page_offset;
|
||||
int sector;
|
||||
int retval = ERROR_OK;
|
||||
|
||||
LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
|
||||
__func__, offset, count);
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
if (offset + count > fespi_info->dev->size_in_bytes) {
|
||||
LOG_WARNING("Write past end of flash. Extra data discarded.");
|
||||
count = fespi_info->dev->size_in_bytes - offset;
|
||||
}
|
||||
|
||||
/* Check sector protection */
|
||||
for (sector = 0; sector < bank->num_sectors; sector++) {
|
||||
/* Start offset in or before this sector? */
|
||||
/* End offset in or behind this sector? */
|
||||
if ((offset <
|
||||
(bank->sectors[sector].offset + bank->sectors[sector].size))
|
||||
&& ((offset + count - 1) >= bank->sectors[sector].offset)
|
||||
&& bank->sectors[sector].is_protected) {
|
||||
LOG_ERROR("Flash sector %d protected", sector);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
page_size = fespi_info->dev->pagesize;
|
||||
|
||||
/* unaligned buffer head */
|
||||
if (count > 0 && (offset & 3) != 0) {
|
||||
cur_count = 4 - (offset & 3);
|
||||
if (cur_count > count)
|
||||
cur_count = count;
|
||||
retval = fespi_write_buffer(bank, buffer, offset,
|
||||
cur_count);
|
||||
if (retval != ERROR_OK)
|
||||
goto err;
|
||||
offset += cur_count;
|
||||
buffer += cur_count;
|
||||
count -= cur_count;
|
||||
}
|
||||
|
||||
page_offset = offset % page_size;
|
||||
/* central part, aligned words */
|
||||
while (count >= 4) {
|
||||
/* clip block at page boundary */
|
||||
if (page_offset + count > page_size)
|
||||
cur_count = page_size - page_offset;
|
||||
else
|
||||
cur_count = count & ~3;
|
||||
|
||||
retval = fespi_write_buffer(bank, buffer, offset,
|
||||
cur_count);
|
||||
if (retval != ERROR_OK)
|
||||
goto err;
|
||||
|
||||
page_offset = 0;
|
||||
buffer += cur_count;
|
||||
offset += cur_count;
|
||||
count -= cur_count;
|
||||
|
||||
keep_alive();
|
||||
}
|
||||
|
||||
/* buffer tail */
|
||||
if (count > 0)
|
||||
retval = fespi_write_buffer(bank, buffer, offset, count);
|
||||
|
||||
err:
|
||||
/* Switch to HW mode before return to prompt */
|
||||
FESPI_ENABLE_HW_MODE();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Return ID of flash device */
|
||||
/* On exit, SW mode is kept */
|
||||
static int fespi_read_flash_id(struct flash_bank *bank, uint32_t *id)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base = fespi_info->ctrl_base;
|
||||
int retval;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
fespi_flush(bank);
|
||||
|
||||
/* Disable Hardware accesses*/
|
||||
FESPI_DISABLE_HW_MODE();
|
||||
|
||||
/* poll WIP */
|
||||
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* Send SPI command "read ID" */
|
||||
fespi_tx(bank, SPIFLASH_READ_ID);
|
||||
|
||||
/* read ID from Receive Register */
|
||||
*id = fespi_rx(bank) & 0x00ffffff;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int fespi_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
uint32_t ctrl_base;
|
||||
struct flash_sector *sectors;
|
||||
uint32_t id = 0; /* silence uninitialized warning */
|
||||
const struct fespi_target *target_device;
|
||||
int retval;
|
||||
|
||||
if (fespi_info->probed)
|
||||
free(bank->sectors);
|
||||
fespi_info->probed = 0;
|
||||
|
||||
for (target_device = target_devices ; target_device->name ; ++target_device)
|
||||
if (target_device->tap_idcode == target->tap->idcode)
|
||||
break;
|
||||
if (!target_device->name) {
|
||||
LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable",
|
||||
target->tap->idcode);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
ctrl_base = target_device->ctrl_base;
|
||||
fespi_info->ctrl_base = ctrl_base;
|
||||
|
||||
LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32,
|
||||
target_device->name, bank->base);
|
||||
|
||||
/* read and decode flash ID; returns in SW mode */
|
||||
retval = fespi_read_flash_id(bank, &id);
|
||||
|
||||
FESPI_ENABLE_HW_MODE();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
fespi_info->dev = NULL;
|
||||
for (const struct flash_device *p = flash_devices; p->name ; p++)
|
||||
if (p->device_id == id) {
|
||||
fespi_info->dev = p;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fespi_info->dev) {
|
||||
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
|
||||
fespi_info->dev->name, fespi_info->dev->device_id);
|
||||
|
||||
/* Set correct size value */
|
||||
bank->size = fespi_info->dev->size_in_bytes;
|
||||
|
||||
/* create and fill sectors array */
|
||||
bank->num_sectors =
|
||||
fespi_info->dev->size_in_bytes / fespi_info->dev->sectorsize;
|
||||
sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
|
||||
if (sectors == NULL) {
|
||||
LOG_ERROR("not enough memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
for (int sector = 0; sector < bank->num_sectors; sector++) {
|
||||
sectors[sector].offset = sector * fespi_info->dev->sectorsize;
|
||||
sectors[sector].size = fespi_info->dev->sectorsize;
|
||||
sectors[sector].is_erased = -1;
|
||||
sectors[sector].is_protected = 1;
|
||||
}
|
||||
|
||||
bank->sectors = sectors;
|
||||
fespi_info->probed = 1;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int fespi_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
if (fespi_info->probed)
|
||||
return ERROR_OK;
|
||||
return fespi_probe(bank);
|
||||
}
|
||||
|
||||
static int fespi_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
/* Nothing to do. Protection is only handled in SW. */
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int get_fespi_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
struct fespi_flash_bank *fespi_info = bank->driver_priv;
|
||||
|
||||
if (!(fespi_info->probed)) {
|
||||
snprintf(buf, buf_size,
|
||||
"\nFESPI flash bank not probed yet\n");
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
snprintf(buf, buf_size, "\nFESPI flash information:\n"
|
||||
" Device \'%s\' (ID 0x%08" PRIx32 ")\n",
|
||||
fespi_info->dev->name, fespi_info->dev->device_id);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct flash_driver fespi_flash = {
|
||||
.name = "fespi",
|
||||
.flash_bank_command = fespi_flash_bank_command,
|
||||
.erase = fespi_erase,
|
||||
.protect = fespi_protect,
|
||||
.write = fespi_write,
|
||||
.read = default_flash_read,
|
||||
.probe = fespi_probe,
|
||||
.auto_probe = fespi_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = fespi_protect_check,
|
||||
.info = get_fespi_info
|
||||
};
|
|
@ -69,6 +69,7 @@ const struct flash_device flash_devices[] = {
|
|||
FLASH_ID("mac 25l6405", 0xd8, 0xc7, 0x001720c2, 0x100, 0x10000, 0x800000),
|
||||
FLASH_ID("micron n25q064", 0xd8, 0xc7, 0x0017ba20, 0x100, 0x10000, 0x800000),
|
||||
FLASH_ID("micron n25q128", 0xd8, 0xc7, 0x0018ba20, 0x100, 0x10000, 0x1000000),
|
||||
FLASH_ID("issi is25lp128", 0xd8, 0xc7, 0xFFFFFFFF, 0x100, 0x10000, 0x1000000), //!!!TODO
|
||||
FLASH_ID("win w25q80bv", 0xd8, 0xc7, 0x001440ef, 0x100, 0x10000, 0x100000),
|
||||
FLASH_ID("win w25q32fv", 0xd8, 0xc7, 0x001640ef, 0x100, 0x10000, 0x400000),
|
||||
FLASH_ID("win w25q32dw", 0xd8, 0xc7, 0x001660ef, 0x100, 0x10000, 0x400000),
|
||||
|
|
Loading…
Reference in New Issue