Line 28... |
Line 28... |
#include <stdio.h>
|
#include <stdio.h>
|
#include <xspi.h>
|
#include <xspi.h>
|
#include "spansion_flash.h"
|
#include "spansion_flash.h"
|
#include "uprintf.h"
|
#include "uprintf.h"
|
|
|
|
#define SPANSION_CFG_REG_QUAD (1<<1)
|
|
#define SPANSION_CFG_REG_OTP_MASK (0x3c)
|
|
#define SPANSION_BANK_REG_EXT_ADDR (1<<7)
|
|
#define SPANSION_CFG_REG_BP_SHIFT 2
|
|
#define SPANSION_CFG_REG_BP_MASK (0x7 << SPANSION_CFG_REG_BP_SHIFT)
|
|
#define SPANSION_STAT_REG_WIP (1<<0)
|
|
#define SPANSION_CFG_REG_LC_MASK (0xc0)
|
|
#define SPANSION_CFG_REG_LC_DEFAULT 0
|
|
static int spansion_flash_cmd_wen(struct spansion_flash *sf);
|
|
static int spansion_flash_get_cr1(struct spansion_flash *sf, u8 *cr1);
|
|
static int spansion_flash_get_sr1(struct spansion_flash *sf, u8 *sr1);
|
|
|
int spansion_flash_init (struct spansion_flash *sf, XSpi *spi, int slave_num)
|
int spansion_flash_init (struct spansion_flash *sf, XSpi *spi, int slave_num)
|
{
|
{
|
|
u8 rCfg;
|
|
|
sf->spi = spi;
|
sf->spi = spi;
|
sf->slave_select = (1<<slave_num);
|
sf->slave_select = (1<<slave_num);
|
int res;
|
int ret;
|
|
|
res = XSpi_SetOptions(sf->spi, (XSP_MASTER_OPTION | \
|
ret = XSpi_SetOptions(sf->spi, (XSP_MASTER_OPTION | \
|
XSP_MANUAL_SSELECT_OPTION | \
|
XSP_MANUAL_SSELECT_OPTION | \
|
XSP_CLK_PHASE_1_OPTION | \
|
XSP_CLK_PHASE_1_OPTION | \
|
XSP_CLK_ACTIVE_LOW_OPTION));
|
XSP_CLK_ACTIVE_LOW_OPTION));
|
if(res)
|
if(ret)
|
return res;
|
return ret;
|
|
|
res = XSpi_SetSlaveSelect(sf->spi, sf->slave_select);
|
ret = XSpi_SetSlaveSelect(sf->spi, sf->slave_select);
|
if(res)
|
if(ret)
|
return res;
|
return ret;
|
|
|
res = XSpi_Start(sf->spi);
|
ret = XSpi_Start(sf->spi);
|
return res;
|
if (ret)
|
|
return ret;
|
|
|
|
/* Put spi to polled mode */
|
|
XSpi_IntrGlobalDisable(sf->spi);
|
|
|
|
/* Check latency is as expected */
|
|
ret = spansion_flash_get_cr1(sf, &rCfg);
|
|
if(ret)
|
|
return ret;
|
|
|
|
if ((rCfg & SPANSION_CFG_REG_LC_MASK) != SPANSION_CFG_REG_LC_DEFAULT) {
|
|
uprintf("Flash is configured with non-default latency code. Cannot read flash\r\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
}
|
}
|
|
|
static int spansion_flash_transfer(struct spansion_flash *sf, uint8_t *wr_buf, uint8_t *rd_buf, int len)
|
static int spansion_flash_transfer(struct spansion_flash *sf, uint8_t *wr_buf, uint8_t *rd_buf, int len)
|
{
|
{
|
int res;
|
int res;
|
Line 62... |
Line 90... |
|
|
return XST_SUCCESS;
|
return XST_SUCCESS;
|
}
|
}
|
|
|
|
|
/* Get status and control register and set QSPI*/
|
/*
|
|
* Get status and control register value
|
|
* and set QSPI mode. It needs to be done
|
|
* after each configuration flash programming
|
|
* using Xilinx tools as they erase this flag.
|
|
*
|
|
* !!!BEWARE: This is potentially dangerous
|
|
* operation as if power is lost during write to
|
|
* status and configuration register, both can be
|
|
* set to all ones. It may set OTP bits that may render
|
|
* flash unusable. As quad mode flag is non-volatile
|
|
* it is necessary to do this once only during board
|
|
*
|
|
*/
|
int spansion_flash_quad_mode(struct spansion_flash *sf)
|
int spansion_flash_quad_mode(struct spansion_flash *sf)
|
{
|
{
|
#define SPANSION_CFG_REG_QUAD (1<<1)
|
|
int ret;
|
int ret;
|
u8 rCfgBuf[2];
|
int tmo_cnt;
|
u8 rStBuf[2];
|
u8 rCfg;
|
|
u8 rSt;
|
u8 wCfgBuf[3];
|
u8 wCfgBuf[3];
|
u8 wEnBuf[1];
|
|
|
|
rCfgBuf[0] = 0x35;
|
ret = spansion_flash_get_cr1(sf, &rCfg);
|
ret = spansion_flash_transfer(sf, rCfgBuf, rCfgBuf, 2);
|
|
if(ret)
|
if(ret)
|
return ret;
|
return ret;
|
|
|
rStBuf[0] = 0x05;
|
if (rCfg & SPANSION_CFG_REG_QUAD) {
|
ret = spansion_flash_transfer(sf, rStBuf, rStBuf, 2);
|
return 0;
|
|
}
|
|
|
|
ret = spansion_flash_get_sr1(sf, &rSt);
|
if(ret)
|
if(ret)
|
return ret;
|
return ret;
|
|
|
wEnBuf[0] = 0x6;
|
ret = spansion_flash_cmd_wen(sf);
|
ret = spansion_flash_transfer(sf, wEnBuf, wEnBuf, 1);
|
|
if(ret)
|
if(ret)
|
return ret;
|
return ret;
|
|
|
|
|
|
uprintf("WARNING: Setting flash quad mode in flash configuration\r\n"
|
|
"registers. If power is lost at that moment flash can be\r\n"
|
|
"corrupted permanently.\r\n");
|
wCfgBuf[0] = 0x01;
|
wCfgBuf[0] = 0x01;
|
wCfgBuf[1] = rStBuf[1];
|
wCfgBuf[1] = rSt;
|
wCfgBuf[2] = rCfgBuf[1] | SPANSION_CFG_REG_QUAD;
|
wCfgBuf[2] = rCfg | SPANSION_CFG_REG_QUAD;
|
|
|
ret = spansion_flash_transfer(sf, wCfgBuf, wCfgBuf, 3);
|
ret = spansion_flash_transfer(sf, wCfgBuf, wCfgBuf, 3);
|
if(ret)
|
if(ret)
|
return ret;
|
return ret;
|
|
|
/* Reread CFG reg */
|
/* Wait until flash finishes write with timeout */
|
rCfgBuf[0] = 0x35;
|
tmo_cnt = 1000;
|
ret = spansion_flash_transfer(sf, rCfgBuf, rCfgBuf, 2);
|
do {
|
|
ret = spansion_flash_get_sr1(sf, &rSt);
|
if(ret)
|
if(ret)
|
return ret;
|
return ret;
|
|
|
return 0;
|
if(tmo_cnt)
|
}
|
tmo_cnt--;
|
|
else
|
/* Set extended addressing mode in Flash and in Xilisf */
|
return -1;
|
/* !!! Doesn't work well for quad command 6b */
|
} while (rSt & SPANSION_STAT_REG_WIP);
|
int spansion_flash_extended_addressing(struct spansion_flash *sf, int onOff)
|
|
{
|
|
#define SPANSION_BANK_REG_EXT_ADDR (1<<7)
|
|
int ret;
|
|
u8 buf[2];
|
|
XSpi *spi = sf->spi;
|
|
|
|
/* Read bank register*/
|
/* Reread CFG reg */
|
buf[0] = 0x16;
|
ret = spansion_flash_get_cr1(sf, &rCfg);
|
ret = XSpi_Transfer(spi, buf, buf, 2);
|
|
if(ret)
|
if(ret)
|
return ret;
|
return ret;
|
|
|
/* Write bank register with extended addressing setting */
|
if (rCfg & SPANSION_CFG_REG_QUAD) {
|
buf[0] = 0x17;
|
uprintf("Flash quad mode set successfully.\r\n");
|
if (onOff) {
|
return 0;
|
buf[1] |= SPANSION_BANK_REG_EXT_ADDR;
|
|
} else {
|
} else {
|
buf[1] &= ~SPANSION_BANK_REG_EXT_ADDR;
|
uprintf("Flash quad mode was not set, cannot read flash.\r\n");
|
|
return -1;
|
}
|
}
|
ret = XSpi_Transfer(spi, buf, buf, 2);
|
|
if(ret)
|
|
return ret;
|
|
|
|
/* Read bank register*/
|
|
buf[0] = 0x16;
|
|
ret = XSpi_Transfer(spi, buf, buf, 2);
|
|
if(ret)
|
|
return ret;
|
|
uprintf("Modified bank register 0x%02x\r\n", buf[1]);
|
|
|
|
return 0;
|
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Use Quad IO 4 byte address read command to read block of data.
|
* Use Quad IO 4 byte address read command to read block of data.
|
* spi - SPI core instance to be used
|
* spi - SPI core instance to be used
|
* flash_addr - 32 bit start address in flash
|
* flash_addr - 32 bit start address in flash
|
* buf - pointer to buffer where data shall be stored; must be 8 byte longer then length of data
|
* buf - pointer to buffer where data shall be stored; must be 8 byte longer then length of data
|
Line 169... |
Line 197... |
*(cmdbuf++) = 0;
|
*(cmdbuf++) = 0;
|
|
|
return XSpi_Transfer(spi, buf, buf, len + 8);
|
return XSpi_Transfer(spi, buf, buf, len + 8);
|
}
|
}
|
|
|
No newline at end of file
|
No newline at end of file
|
|
/*
|
|
* Read and print flash configuration and status.
|
|
*/
|
|
int spansion_flash_print_status(struct spansion_flash *sf)
|
|
{
|
|
int ret;
|
|
u8 rCfgBuf[2];
|
|
u8 rStBuf[2];
|
|
u8 rRDID[4];
|
|
|
|
rRDID[0] = 0x9f;
|
|
ret = spansion_flash_transfer(sf, rRDID, rRDID, 4);
|
|
if(ret)
|
|
return ret;
|
|
uprintf("RDID: 0x%02x 0x%02x 0x%02x\r\n", rRDID[1], rRDID[2], rRDID[3]);
|
|
|
|
rStBuf[0] = 0x05;
|
|
ret = spansion_flash_transfer(sf, rStBuf, rStBuf, 2);
|
|
if(ret)
|
|
return ret;
|
|
uprintf("Status reg 1: 0x%02x\r\n", rStBuf[1]);
|
|
|
|
rStBuf[0] = 0x07;
|
|
ret = spansion_flash_transfer(sf, rStBuf, rStBuf, 2);
|
|
if(ret)
|
|
return ret;
|
|
uprintf("Status reg 2: 0x%02x\r\n", rStBuf[1]);
|
|
|
|
rCfgBuf[0] = 0x35;
|
|
ret = spansion_flash_transfer(sf, rCfgBuf, rCfgBuf, 2);
|
|
if(ret)
|
|
return ret;
|
|
uprintf("Configuration reg: 0x%02x\r\n", rCfgBuf[1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Send write enable command - needed for register write */
|
|
static int spansion_flash_cmd_wen(struct spansion_flash *sf)
|
|
{
|
|
int ret;
|
|
u8 wEnBuf[1];
|
|
|
|
wEnBuf[0] = 0x6;
|
|
ret = spansion_flash_transfer(sf, wEnBuf, wEnBuf, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Send Bulk erase command*/
|
|
static int spansion_flash_cmd_be(struct spansion_flash *sf)
|
|
{
|
|
int ret;
|
|
u8 buf[1];
|
|
|
|
buf[0] = 0x60;
|
|
ret = spansion_flash_transfer(sf, buf, buf, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Send get status register sr1*/
|
|
static int spansion_flash_get_sr1(struct spansion_flash *sf, u8 *sr1)
|
|
{
|
|
int ret;
|
|
u8 rStBuf[2];
|
|
|
|
rStBuf[0] = 0x05;
|
|
ret = spansion_flash_transfer(sf, rStBuf, rStBuf, 2);
|
|
|
|
*sr1 = rStBuf[1];
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Send get configuration register */
|
|
static int spansion_flash_get_cr1(struct spansion_flash *sf, u8 *cr1)
|
|
{
|
|
int ret;
|
|
u8 rCfgBuf[2];
|
|
|
|
rCfgBuf[0] = 0x35;
|
|
ret = spansion_flash_transfer(sf, rCfgBuf, rCfgBuf, 2);
|
|
|
|
*cr1 = rCfgBuf[1];
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Erase whole device using bulk erase.
|
|
*/
|
|
int spansion_flash_bulk_erase(struct spansion_flash *sf)
|
|
{
|
|
int ret;
|
|
u8 sr;
|
|
int i;
|
|
|
|
ret = spansion_flash_cmd_wen(sf);
|
|
if(ret)
|
|
return ret;
|
|
|
|
ret = spansion_flash_cmd_be(sf);
|
|
if(ret)
|
|
return ret;
|
|
|
|
i = 0;
|
|
do {
|
|
ret = spansion_flash_get_sr1(sf, &sr);
|
|
i++;
|
|
} while (sr & SPANSION_STAT_REG_WIP);
|
|
|
|
return ret;
|
|
}
|
|
|
No newline at end of file
|
No newline at end of file
|