2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2013-08-29 13:31:56 +00:00
|
|
|
/*
|
2015-12-06 17:59:02 +00:00
|
|
|
* SPI Flash Core
|
2013-08-29 13:31:56 +00:00
|
|
|
*
|
2015-12-06 17:59:02 +00:00
|
|
|
* Copyright (C) 2015 Jagan Teki <jteki@openedev.com>
|
2013-08-29 13:31:56 +00:00
|
|
|
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
|
2015-12-06 17:59:02 +00:00
|
|
|
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
|
|
|
|
* Copyright (C) 2008 Atmel Corporation
|
2013-08-29 13:31:56 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
2014-02-04 16:06:13 +00:00
|
|
|
#include <errno.h>
|
2014-01-11 11:20:45 +00:00
|
|
|
#include <malloc.h>
|
2015-12-11 16:06:34 +00:00
|
|
|
#include <mapmem.h>
|
2013-08-29 13:31:56 +00:00
|
|
|
#include <spi.h>
|
|
|
|
#include <spi_flash.h>
|
2015-11-05 14:43:41 +00:00
|
|
|
#include <linux/log2.h>
|
2018-04-10 11:40:44 +00:00
|
|
|
#include <linux/sizes.h>
|
2016-02-15 10:01:39 +00:00
|
|
|
#include <dma.h>
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2013-09-26 10:30:15 +00:00
|
|
|
#include "sf_internal.h"
|
2013-08-29 13:31:56 +00:00
|
|
|
|
|
|
|
static void spi_flash_addr(u32 addr, u8 *cmd)
|
|
|
|
{
|
|
|
|
/* cmd[0] is actual command */
|
|
|
|
cmd[1] = addr >> 16;
|
|
|
|
cmd[2] = addr >> 8;
|
|
|
|
cmd[3] = addr >> 0;
|
|
|
|
}
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
static int read_sr(struct spi_flash *flash, u8 *rs)
|
2013-08-29 13:31:56 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2013-12-30 16:46:23 +00:00
|
|
|
u8 cmd;
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2013-12-30 16:46:23 +00:00
|
|
|
cmd = CMD_READ_STATUS;
|
|
|
|
ret = spi_flash_read_common(flash, &cmd, 1, rs, 1);
|
2013-08-29 13:31:56 +00:00
|
|
|
if (ret < 0) {
|
2013-12-30 16:46:23 +00:00
|
|
|
debug("SF: fail to read status register\n");
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-29 11:24:31 +00:00
|
|
|
static int read_fsr(struct spi_flash *flash, u8 *fsr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const u8 cmd = CMD_FLAG_STATUS;
|
|
|
|
|
|
|
|
ret = spi_flash_read_common(flash, &cmd, 1, fsr, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: fail to read flag status register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
static int write_sr(struct spi_flash *flash, u8 ws)
|
2013-12-26 08:43:36 +00:00
|
|
|
{
|
|
|
|
u8 cmd;
|
|
|
|
int ret;
|
|
|
|
|
2013-12-30 16:46:23 +00:00
|
|
|
cmd = CMD_WRITE_STATUS;
|
2014-01-12 16:08:21 +00:00
|
|
|
ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1);
|
2013-12-26 08:43:36 +00:00
|
|
|
if (ret < 0) {
|
2013-12-30 16:46:23 +00:00
|
|
|
debug("SF: fail to write status register\n");
|
2013-12-26 08:43:36 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-30 16:46:23 +00:00
|
|
|
return 0;
|
2013-12-26 08:43:36 +00:00
|
|
|
}
|
|
|
|
|
2013-12-26 08:24:57 +00:00
|
|
|
#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
|
2015-09-29 16:59:33 +00:00
|
|
|
static int read_cr(struct spi_flash *flash, u8 *rc)
|
2013-12-23 10:17:48 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2013-12-30 16:46:23 +00:00
|
|
|
u8 cmd;
|
2013-12-23 10:17:48 +00:00
|
|
|
|
2013-12-30 16:46:23 +00:00
|
|
|
cmd = CMD_READ_CONFIG;
|
|
|
|
ret = spi_flash_read_common(flash, &cmd, 1, rc, 1);
|
2013-12-23 10:17:48 +00:00
|
|
|
if (ret < 0) {
|
2013-12-30 16:46:23 +00:00
|
|
|
debug("SF: fail to read config register\n");
|
2013-12-23 10:17:48 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
static int write_cr(struct spi_flash *flash, u8 wc)
|
2013-12-26 08:24:57 +00:00
|
|
|
{
|
2013-12-30 16:46:23 +00:00
|
|
|
u8 data[2];
|
2013-12-26 08:24:57 +00:00
|
|
|
u8 cmd;
|
|
|
|
int ret;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
ret = read_sr(flash, &data[0]);
|
2013-12-30 16:46:23 +00:00
|
|
|
if (ret < 0)
|
2013-12-26 08:24:57 +00:00
|
|
|
return ret;
|
|
|
|
|
2013-12-30 16:46:23 +00:00
|
|
|
cmd = CMD_WRITE_STATUS;
|
|
|
|
data[1] = wc;
|
|
|
|
ret = spi_flash_write_common(flash, &cmd, 1, &data, 2);
|
|
|
|
if (ret) {
|
|
|
|
debug("SF: fail to write config register\n");
|
|
|
|
return ret;
|
2013-12-26 08:24:57 +00:00
|
|
|
}
|
|
|
|
|
2013-12-30 16:46:23 +00:00
|
|
|
return 0;
|
2013-12-26 08:24:57 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-08-29 13:31:56 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
2017-09-25 10:40:08 +00:00
|
|
|
/*
|
|
|
|
* This "clean_bar" is necessary in a situation when one was accessing
|
|
|
|
* spi flash memory > 16 MiB by using Bank Address Register's BA24 bit.
|
|
|
|
*
|
|
|
|
* After it the BA24 bit shall be cleared to allow access to correct
|
|
|
|
* memory region after SW reset (by calling "reset" command).
|
|
|
|
*
|
|
|
|
* Otherwise, the BA24 bit may be left set and then after reset, the
|
|
|
|
* ROM would read/write/erase SPL from 16 MiB * bank_sel address.
|
|
|
|
*/
|
|
|
|
static int clean_bar(struct spi_flash *flash)
|
|
|
|
{
|
|
|
|
u8 cmd, bank_sel = 0;
|
|
|
|
|
|
|
|
if (flash->bank_curr == 0)
|
|
|
|
return 0;
|
|
|
|
cmd = flash->bank_write_cmd;
|
sf: Set current flash bank to 0 in clean_bar()
The clean_bar() function resets the SPI NOR BAR register to 0, but
does not set the flash->curr_bar to 0 , therefore those two can get
out of sync, which could ultimatelly result in corrupted flash content.
The simplest test case is this:
=> mw 0x10000000 0x1234abcd 0x4000
=> sf probe
=> sf erase 0x1000000 0x10000
=> sf write 0x10000000 0x1000000 0x10000
=> sf probe ; sf read 0x12000000 0 0x10000 ; md 0x12000000
That is, erase a sector above the 16 MiB boundary and write it with
random pre-configured data. What will actually happen without this
patch is the sector will be erased, but the data will be written to
BAR 0 offset 0x0 in the flash.
This is because the erase command will call write_bar()+clean_bar(),
which will leave flash->bank_curr = 1 while the hardware BAR registers
will be set to 0 through clean_bar(). The subsequent write will also
trigger write_bar()+clean_bar(), but write_bar checks if the target
bank == flash->bank_curr and if so, does NOT reconfigure the BAR in
the SPI NOR. Since flash->bank_curr is still 1 and out of sync with
the HW, the condition matches, BAR programming is skipped and write
ends up at address 0x0, thus corrupting flash content.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Tom Rini <trini@konsulko.com>
Reviewed-by: Jagan Teki <jagan@openedev.com>
2018-05-24 19:58:40 +00:00
|
|
|
flash->bank_curr = 0;
|
2017-09-25 10:40:08 +00:00
|
|
|
|
|
|
|
return spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
|
|
|
|
}
|
|
|
|
|
2016-10-30 17:46:25 +00:00
|
|
|
static int write_bar(struct spi_flash *flash, u32 offset)
|
2013-08-29 13:31:56 +00:00
|
|
|
{
|
2015-09-02 06:09:48 +00:00
|
|
|
u8 cmd, bank_sel;
|
2013-08-29 13:31:56 +00:00
|
|
|
int ret;
|
|
|
|
|
2015-09-02 06:09:48 +00:00
|
|
|
bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift);
|
|
|
|
if (bank_sel == flash->bank_curr)
|
|
|
|
goto bar_end;
|
2013-08-29 13:31:56 +00:00
|
|
|
|
|
|
|
cmd = flash->bank_write_cmd;
|
|
|
|
ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: fail to write bank register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
2013-10-08 17:56:47 +00:00
|
|
|
|
2015-09-02 06:09:48 +00:00
|
|
|
bar_end:
|
|
|
|
flash->bank_curr = bank_sel;
|
|
|
|
return flash->bank_curr;
|
2013-10-08 17:56:47 +00:00
|
|
|
}
|
2015-11-03 18:57:35 +00:00
|
|
|
|
2016-10-30 17:46:25 +00:00
|
|
|
static int read_bar(struct spi_flash *flash, const struct spi_flash_info *info)
|
2015-11-03 18:57:35 +00:00
|
|
|
{
|
|
|
|
u8 curr_bank = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (flash->size <= SPI_FLASH_16MB_BOUN)
|
2015-12-13 17:40:33 +00:00
|
|
|
goto bar_end;
|
2015-11-03 18:57:35 +00:00
|
|
|
|
2016-10-30 17:46:10 +00:00
|
|
|
switch (JEDEC_MFR(info)) {
|
2015-11-03 18:57:35 +00:00
|
|
|
case SPI_FLASH_CFI_MFR_SPANSION:
|
|
|
|
flash->bank_read_cmd = CMD_BANKADDR_BRRD;
|
|
|
|
flash->bank_write_cmd = CMD_BANKADDR_BRWR;
|
2015-11-20 07:30:15 +00:00
|
|
|
break;
|
2015-11-03 18:57:35 +00:00
|
|
|
default:
|
|
|
|
flash->bank_read_cmd = CMD_EXTNADDR_RDEAR;
|
|
|
|
flash->bank_write_cmd = CMD_EXTNADDR_WREAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = spi_flash_read_common(flash, &flash->bank_read_cmd, 1,
|
|
|
|
&curr_bank, 1);
|
|
|
|
if (ret) {
|
|
|
|
debug("SF: fail to read bank addr register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-12-13 17:40:33 +00:00
|
|
|
bar_end:
|
2015-11-03 18:57:35 +00:00
|
|
|
flash->bank_curr = curr_bank;
|
|
|
|
return 0;
|
|
|
|
}
|
2013-08-29 13:31:56 +00:00
|
|
|
#endif
|
|
|
|
|
2014-01-11 09:55:04 +00:00
|
|
|
#ifdef CONFIG_SF_DUAL_FLASH
|
2015-09-29 16:59:33 +00:00
|
|
|
static void spi_flash_dual(struct spi_flash *flash, u32 *addr)
|
2014-01-12 16:10:11 +00:00
|
|
|
{
|
|
|
|
switch (flash->dual_flash) {
|
|
|
|
case SF_DUAL_STACKED_FLASH:
|
|
|
|
if (*addr >= (flash->size >> 1)) {
|
|
|
|
*addr -= flash->size >> 1;
|
2016-10-30 17:46:26 +00:00
|
|
|
flash->flags |= SNOR_F_USE_UPAGE;
|
2014-01-12 16:10:11 +00:00
|
|
|
} else {
|
2016-10-30 17:46:26 +00:00
|
|
|
flash->flags &= ~SNOR_F_USE_UPAGE;
|
2014-01-12 16:10:11 +00:00
|
|
|
}
|
|
|
|
break;
|
2014-01-06 18:41:35 +00:00
|
|
|
case SF_DUAL_PARALLEL_FLASH:
|
|
|
|
*addr >>= flash->shift;
|
|
|
|
break;
|
2014-01-12 16:10:11 +00:00
|
|
|
default:
|
|
|
|
debug("SF: Unsupported dual_flash=%d\n", flash->dual_flash);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-01-11 09:55:04 +00:00
|
|
|
#endif
|
2014-01-12 16:10:11 +00:00
|
|
|
|
2015-09-29 11:24:31 +00:00
|
|
|
static int spi_flash_sr_ready(struct spi_flash *flash)
|
2015-03-11 09:17:57 +00:00
|
|
|
{
|
2015-09-02 06:09:50 +00:00
|
|
|
u8 sr;
|
2015-09-29 11:24:31 +00:00
|
|
|
int ret;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
ret = read_sr(flash, &sr);
|
2015-09-29 11:24:31 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return !(sr & STATUS_WIP);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int spi_flash_fsr_ready(struct spi_flash *flash)
|
|
|
|
{
|
|
|
|
u8 fsr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = read_fsr(flash, &fsr);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return fsr & STATUS_PEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int spi_flash_ready(struct spi_flash *flash)
|
|
|
|
{
|
|
|
|
int sr, fsr;
|
|
|
|
|
|
|
|
sr = spi_flash_sr_ready(flash);
|
|
|
|
if (sr < 0)
|
|
|
|
return sr;
|
|
|
|
|
|
|
|
fsr = 1;
|
|
|
|
if (flash->flags & SNOR_F_USE_FSR) {
|
|
|
|
fsr = spi_flash_fsr_ready(flash);
|
|
|
|
if (fsr < 0)
|
|
|
|
return fsr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sr && fsr;
|
|
|
|
}
|
|
|
|
|
2016-10-30 17:46:25 +00:00
|
|
|
static int spi_flash_wait_till_ready(struct spi_flash *flash,
|
|
|
|
unsigned long timeout)
|
2015-09-29 11:24:31 +00:00
|
|
|
{
|
2016-04-04 17:03:52 +00:00
|
|
|
unsigned long timebase;
|
|
|
|
int ret;
|
2015-03-11 09:17:57 +00:00
|
|
|
|
2015-09-02 06:09:50 +00:00
|
|
|
timebase = get_timer(0);
|
2015-03-11 09:17:57 +00:00
|
|
|
|
2015-09-02 06:09:50 +00:00
|
|
|
while (get_timer(timebase) < timeout) {
|
2015-09-29 11:24:31 +00:00
|
|
|
ret = spi_flash_ready(flash);
|
2015-03-11 09:17:57 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2015-09-29 11:24:31 +00:00
|
|
|
if (ret)
|
2015-09-02 06:09:50 +00:00
|
|
|
return 0;
|
2015-03-11 09:17:57 +00:00
|
|
|
}
|
|
|
|
|
2015-09-02 06:09:50 +00:00
|
|
|
printf("SF: Timeout!\n");
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
2015-03-11 09:17:57 +00:00
|
|
|
}
|
|
|
|
|
2013-08-29 13:31:56 +00:00
|
|
|
int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
|
|
|
|
size_t cmd_len, const void *buf, size_t buf_len)
|
|
|
|
{
|
|
|
|
struct spi_slave *spi = flash->spi;
|
|
|
|
unsigned long timeout = SPI_FLASH_PROG_TIMEOUT;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (buf == NULL)
|
|
|
|
timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_claim_bus(spi);
|
2013-08-29 13:31:56 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("SF: unable to claim SPI bus\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = spi_flash_cmd_write_enable(flash);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: enabling write failed\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: write cmd failed\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-10-30 17:46:25 +00:00
|
|
|
ret = spi_flash_wait_till_ready(flash, timeout);
|
2013-08-29 13:31:56 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: write %s timed out\n",
|
|
|
|
timeout == SPI_FLASH_PROG_TIMEOUT ?
|
|
|
|
"program" : "page erase");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
spi_release_bus(spi);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-02 14:08:49 +00:00
|
|
|
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
|
2013-08-29 13:31:56 +00:00
|
|
|
{
|
2014-01-12 16:10:11 +00:00
|
|
|
u32 erase_size, erase_addr;
|
2014-01-11 11:20:45 +00:00
|
|
|
u8 cmd[SPI_FLASH_CMD_LEN];
|
2013-08-29 13:31:56 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
2013-10-02 14:06:58 +00:00
|
|
|
erase_size = flash->erase_size;
|
2013-08-29 13:31:56 +00:00
|
|
|
if (offset % erase_size || len % erase_size) {
|
2018-03-14 23:15:10 +00:00
|
|
|
printf("SF: Erase offset/length not multiple of erase size\n");
|
2013-08-29 13:31:56 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-11-13 10:46:26 +00:00
|
|
|
if (flash->flash_is_locked) {
|
|
|
|
if (flash->flash_is_locked(flash, offset, len) > 0) {
|
|
|
|
printf("offset 0x%x is protected and cannot be erased\n",
|
|
|
|
offset);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-11-05 14:43:42 +00:00
|
|
|
}
|
|
|
|
|
2013-10-02 14:06:58 +00:00
|
|
|
cmd[0] = flash->erase_cmd;
|
2013-08-29 13:31:56 +00:00
|
|
|
while (len) {
|
2014-01-12 16:10:11 +00:00
|
|
|
erase_addr = offset;
|
|
|
|
|
2014-01-11 09:55:04 +00:00
|
|
|
#ifdef CONFIG_SF_DUAL_FLASH
|
2014-01-12 16:10:11 +00:00
|
|
|
if (flash->dual_flash > SF_SINGLE_FLASH)
|
2015-09-29 16:59:33 +00:00
|
|
|
spi_flash_dual(flash, &erase_addr);
|
2014-01-11 09:55:04 +00:00
|
|
|
#endif
|
2013-08-29 13:31:56 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
2016-10-30 17:46:25 +00:00
|
|
|
ret = write_bar(flash, erase_addr);
|
2013-10-08 17:56:47 +00:00
|
|
|
if (ret < 0)
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
|
|
|
#endif
|
2014-01-12 16:10:11 +00:00
|
|
|
spi_flash_addr(erase_addr, cmd);
|
2013-08-29 13:31:56 +00:00
|
|
|
|
|
|
|
debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
|
2014-01-12 16:10:11 +00:00
|
|
|
cmd[2], cmd[3], erase_addr);
|
2013-08-29 13:31:56 +00:00
|
|
|
|
|
|
|
ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: erase failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += erase_size;
|
|
|
|
len -= erase_size;
|
|
|
|
}
|
|
|
|
|
2017-09-25 10:40:08 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
|
|
|
ret = clean_bar(flash);
|
|
|
|
#endif
|
|
|
|
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-02 14:08:49 +00:00
|
|
|
int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
|
2013-08-29 13:31:56 +00:00
|
|
|
size_t len, const void *buf)
|
|
|
|
{
|
2015-12-12 06:21:57 +00:00
|
|
|
struct spi_slave *spi = flash->spi;
|
2013-08-29 13:31:56 +00:00
|
|
|
unsigned long byte_addr, page_size;
|
2014-01-12 16:10:11 +00:00
|
|
|
u32 write_addr;
|
2013-08-29 13:31:56 +00:00
|
|
|
size_t chunk_len, actual;
|
2014-01-11 11:20:45 +00:00
|
|
|
u8 cmd[SPI_FLASH_CMD_LEN];
|
2013-08-29 13:31:56 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
page_size = flash->page_size;
|
|
|
|
|
2015-11-13 10:46:26 +00:00
|
|
|
if (flash->flash_is_locked) {
|
|
|
|
if (flash->flash_is_locked(flash, offset, len) > 0) {
|
|
|
|
printf("offset 0x%x is protected and cannot be written\n",
|
|
|
|
offset);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-11-05 14:43:42 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 09:43:11 +00:00
|
|
|
cmd[0] = flash->write_cmd;
|
2013-08-29 13:31:56 +00:00
|
|
|
for (actual = 0; actual < len; actual += chunk_len) {
|
2014-01-12 16:10:11 +00:00
|
|
|
write_addr = offset;
|
|
|
|
|
2014-01-11 09:55:04 +00:00
|
|
|
#ifdef CONFIG_SF_DUAL_FLASH
|
2014-01-12 16:10:11 +00:00
|
|
|
if (flash->dual_flash > SF_SINGLE_FLASH)
|
2015-09-29 16:59:33 +00:00
|
|
|
spi_flash_dual(flash, &write_addr);
|
2014-01-11 09:55:04 +00:00
|
|
|
#endif
|
2013-08-29 13:31:56 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
2016-10-30 17:46:25 +00:00
|
|
|
ret = write_bar(flash, write_addr);
|
2013-10-08 17:56:47 +00:00
|
|
|
if (ret < 0)
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
|
|
|
#endif
|
|
|
|
byte_addr = offset % page_size;
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
chunk_len = min(len - actual, (size_t)(page_size - byte_addr));
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
if (spi->max_write_size)
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
chunk_len = min(chunk_len,
|
2018-01-23 16:14:57 +00:00
|
|
|
spi->max_write_size - sizeof(cmd));
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2014-01-12 16:10:11 +00:00
|
|
|
spi_flash_addr(write_addr, cmd);
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2014-01-12 16:08:21 +00:00
|
|
|
debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
|
2013-08-29 13:31:56 +00:00
|
|
|
buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
|
|
|
|
|
|
|
|
ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
|
|
|
|
buf + actual, chunk_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: write failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += chunk_len;
|
|
|
|
}
|
|
|
|
|
2017-09-25 10:40:08 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
|
|
|
ret = clean_bar(flash);
|
|
|
|
#endif
|
|
|
|
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
|
|
|
|
size_t cmd_len, void *data, size_t data_len)
|
|
|
|
{
|
|
|
|
struct spi_slave *spi = flash->spi;
|
|
|
|
int ret;
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_claim_bus(spi);
|
2013-08-29 13:31:56 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("SF: unable to claim SPI bus\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: read cmd failed\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
spi_release_bus(spi);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-02-15 10:01:39 +00:00
|
|
|
/*
|
|
|
|
* TODO: remove the weak after all the other spi_flash_copy_mmap
|
|
|
|
* implementations removed from drivers
|
|
|
|
*/
|
2015-08-17 07:59:54 +00:00
|
|
|
void __weak spi_flash_copy_mmap(void *data, void *offset, size_t len)
|
|
|
|
{
|
2016-02-15 10:01:39 +00:00
|
|
|
#ifdef CONFIG_DMA
|
|
|
|
if (!dma_memcpy(data, offset, len))
|
|
|
|
return;
|
|
|
|
#endif
|
2015-08-17 07:59:54 +00:00
|
|
|
memcpy(data, offset, len);
|
|
|
|
}
|
|
|
|
|
2013-10-02 14:08:49 +00:00
|
|
|
int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
|
2013-08-29 13:31:56 +00:00
|
|
|
size_t len, void *data)
|
|
|
|
{
|
2015-12-12 06:21:57 +00:00
|
|
|
struct spi_slave *spi = flash->spi;
|
2014-01-11 11:27:07 +00:00
|
|
|
u8 *cmd, cmdsz;
|
2014-01-12 16:10:11 +00:00
|
|
|
u32 remain_len, read_len, read_addr;
|
2014-01-11 11:27:07 +00:00
|
|
|
int bank_sel = 0;
|
2013-08-29 13:31:56 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
/* Handle memory-mapped SPI */
|
|
|
|
if (flash->memory_map) {
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_claim_bus(spi);
|
2013-11-14 15:31:15 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("SF: unable to claim SPI bus\n");
|
|
|
|
return ret;
|
|
|
|
}
|
2015-12-12 06:21:57 +00:00
|
|
|
spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP);
|
2015-08-17 07:59:54 +00:00
|
|
|
spi_flash_copy_mmap(data, flash->memory_map + offset, len);
|
2015-12-12 06:21:57 +00:00
|
|
|
spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END);
|
|
|
|
spi_release_bus(spi);
|
2013-08-29 13:31:56 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-11 11:20:45 +00:00
|
|
|
cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte;
|
2014-02-04 16:06:13 +00:00
|
|
|
cmd = calloc(1, cmdsz);
|
|
|
|
if (!cmd) {
|
|
|
|
debug("SF: Failed to allocate cmd\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2014-01-11 11:20:45 +00:00
|
|
|
cmd[0] = flash->read_cmd;
|
2013-08-29 13:31:56 +00:00
|
|
|
while (len) {
|
2014-01-12 16:10:11 +00:00
|
|
|
read_addr = offset;
|
|
|
|
|
2014-01-11 09:55:04 +00:00
|
|
|
#ifdef CONFIG_SF_DUAL_FLASH
|
2014-01-12 16:10:11 +00:00
|
|
|
if (flash->dual_flash > SF_SINGLE_FLASH)
|
2015-09-29 16:59:33 +00:00
|
|
|
spi_flash_dual(flash, &read_addr);
|
2014-01-11 09:55:04 +00:00
|
|
|
#endif
|
2013-08-29 13:31:56 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
2016-10-30 17:46:25 +00:00
|
|
|
ret = write_bar(flash, read_addr);
|
2015-09-02 06:09:48 +00:00
|
|
|
if (ret < 0)
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
2015-09-02 06:09:48 +00:00
|
|
|
bank_sel = flash->bank_curr;
|
2013-08-29 13:31:56 +00:00
|
|
|
#endif
|
2014-01-06 18:41:35 +00:00
|
|
|
remain_len = ((SPI_FLASH_16MB_BOUN << flash->shift) *
|
|
|
|
(bank_sel + 1)) - offset;
|
2013-08-29 13:31:56 +00:00
|
|
|
if (len < remain_len)
|
|
|
|
read_len = len;
|
|
|
|
else
|
|
|
|
read_len = remain_len;
|
|
|
|
|
2018-01-23 16:14:56 +00:00
|
|
|
if (spi->max_read_size)
|
|
|
|
read_len = min(read_len, spi->max_read_size);
|
|
|
|
|
2014-01-12 16:10:11 +00:00
|
|
|
spi_flash_addr(read_addr, cmd);
|
2013-08-29 13:31:56 +00:00
|
|
|
|
2014-01-11 11:20:45 +00:00
|
|
|
ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len);
|
2013-08-29 13:31:56 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
debug("SF: read failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += read_len;
|
|
|
|
len -= read_len;
|
|
|
|
data += read_len;
|
|
|
|
}
|
|
|
|
|
2017-09-25 10:40:08 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
|
|
|
ret = clean_bar(flash);
|
|
|
|
#endif
|
|
|
|
|
2014-07-12 12:41:31 +00:00
|
|
|
free(cmd);
|
2013-08-29 13:31:56 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2013-10-02 14:04:53 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_SPI_FLASH_SST
|
2018-04-10 11:40:44 +00:00
|
|
|
static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl)
|
|
|
|
{
|
|
|
|
switch (ctl) {
|
|
|
|
case SST26_CTL_LOCK:
|
|
|
|
cmd[bpr_size - (bit / 8) - 1] |= BIT(bit % 8);
|
|
|
|
break;
|
|
|
|
case SST26_CTL_UNLOCK:
|
|
|
|
cmd[bpr_size - (bit / 8) - 1] &= ~BIT(bit % 8);
|
|
|
|
break;
|
|
|
|
case SST26_CTL_CHECK:
|
|
|
|
return !!(cmd[bpr_size - (bit / 8) - 1] & BIT(bit % 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sst26wf016/sst26wf032/sst26wf064 have next block protection:
|
|
|
|
* 4x - 8 KByte blocks - read & write protection bits - upper addresses
|
|
|
|
* 1x - 32 KByte blocks - write protection bits
|
|
|
|
* rest - 64 KByte blocks - write protection bits
|
|
|
|
* 1x - 32 KByte blocks - write protection bits
|
|
|
|
* 4x - 8 KByte blocks - read & write protection bits - lower addresses
|
|
|
|
*
|
|
|
|
* We'll support only per 64k lock/unlock so lower and upper 64 KByte region
|
|
|
|
* will be treated as single block.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lock, unlock or check lock status of the flash region of the flash (depending
|
|
|
|
* on the lock_ctl value)
|
|
|
|
*/
|
|
|
|
static int sst26_lock_ctl(struct spi_flash *flash, u32 ofs, size_t len, enum lock_ctl ctl)
|
|
|
|
{
|
|
|
|
u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size;
|
|
|
|
bool lower_64k = false, upper_64k = false;
|
|
|
|
u8 cmd, bpr_buff[SST26_MAX_BPR_REG_LEN] = {};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Check length and offset for 64k alignment */
|
|
|
|
if ((ofs & (SZ_64K - 1)) || (len & (SZ_64K - 1)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (ofs + len > flash->size)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* SST26 family has only 16 Mbit, 32 Mbit and 64 Mbit IC */
|
|
|
|
if (flash->size != SZ_2M &&
|
|
|
|
flash->size != SZ_4M &&
|
|
|
|
flash->size != SZ_8M)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
bpr_size = 2 + (flash->size / SZ_64K / 8);
|
|
|
|
|
|
|
|
cmd = SST26_CMD_READ_BPR;
|
|
|
|
ret = spi_flash_read_common(flash, &cmd, 1, bpr_buff, bpr_size);
|
|
|
|
if (ret < 0) {
|
|
|
|
printf("SF: fail to read block-protection register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
rptr_64k = min_t(u32, ofs + len , flash->size - SST26_BOUND_REG_SIZE);
|
|
|
|
lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE);
|
|
|
|
|
|
|
|
upper_64k = ((ofs + len) > (flash->size - SST26_BOUND_REG_SIZE));
|
|
|
|
lower_64k = (ofs < SST26_BOUND_REG_SIZE);
|
|
|
|
|
|
|
|
/* Lower bits in block-protection register are about 64k region */
|
|
|
|
bpr_ptr = lptr_64k / SZ_64K - 1;
|
|
|
|
|
|
|
|
/* Process 64K blocks region */
|
|
|
|
while (lptr_64k < rptr_64k) {
|
|
|
|
if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
|
|
|
|
return EACCES;
|
|
|
|
|
|
|
|
bpr_ptr++;
|
|
|
|
lptr_64k += SZ_64K;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 32K and 8K region bits in BPR are after 64k region bits */
|
|
|
|
bpr_ptr = (flash->size - 2 * SST26_BOUND_REG_SIZE) / SZ_64K;
|
|
|
|
|
|
|
|
/* Process lower 32K block region */
|
|
|
|
if (lower_64k)
|
|
|
|
if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
|
|
|
|
return EACCES;
|
|
|
|
|
|
|
|
bpr_ptr++;
|
|
|
|
|
|
|
|
/* Process upper 32K block region */
|
|
|
|
if (upper_64k)
|
|
|
|
if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
|
|
|
|
return EACCES;
|
|
|
|
|
|
|
|
bpr_ptr++;
|
|
|
|
|
|
|
|
/* Process lower 8K block regions */
|
|
|
|
for (i = 0; i < SST26_BPR_8K_NUM; i++) {
|
|
|
|
if (lower_64k)
|
|
|
|
if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
|
|
|
|
return EACCES;
|
|
|
|
|
|
|
|
/* In 8K area BPR has both read and write protection bits */
|
|
|
|
bpr_ptr += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process upper 8K block regions */
|
|
|
|
for (i = 0; i < SST26_BPR_8K_NUM; i++) {
|
|
|
|
if (upper_64k)
|
|
|
|
if (sst26_process_bpr(bpr_size, bpr_buff, bpr_ptr, ctl))
|
|
|
|
return EACCES;
|
|
|
|
|
|
|
|
/* In 8K area BPR has both read and write protection bits */
|
|
|
|
bpr_ptr += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we check region status we don't need to write BPR back */
|
|
|
|
if (ctl == SST26_CTL_CHECK)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cmd = SST26_CMD_WRITE_BPR;
|
|
|
|
ret = spi_flash_write_common(flash, &cmd, 1, bpr_buff, bpr_size);
|
|
|
|
if (ret < 0) {
|
|
|
|
printf("SF: fail to write block-protection register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sst26_unlock(struct spi_flash *flash, u32 ofs, size_t len)
|
|
|
|
{
|
|
|
|
return sst26_lock_ctl(flash, ofs, len, SST26_CTL_UNLOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sst26_lock(struct spi_flash *flash, u32 ofs, size_t len)
|
|
|
|
{
|
|
|
|
return sst26_lock_ctl(flash, ofs, len, SST26_CTL_LOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns EACCES (positive value) if region is locked, 0 if region is unlocked,
|
|
|
|
* and negative on errors.
|
|
|
|
*/
|
|
|
|
static int sst26_is_locked(struct spi_flash *flash, u32 ofs, size_t len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* is_locked function is used for check before reading or erasing flash
|
|
|
|
* region, so offset and length might be not 64k allighned, so adjust
|
|
|
|
* them to be 64k allighned as sst26_lock_ctl works only with 64k
|
|
|
|
* allighned regions.
|
|
|
|
*/
|
|
|
|
ofs -= ofs & (SZ_64K - 1);
|
|
|
|
len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
|
|
|
|
|
|
|
|
return sst26_lock_ctl(flash, ofs, len, SST26_CTL_CHECK);
|
|
|
|
}
|
|
|
|
|
2013-10-02 14:04:53 +00:00
|
|
|
static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
|
|
|
|
{
|
2015-12-12 06:21:57 +00:00
|
|
|
struct spi_slave *spi = flash->spi;
|
2013-10-02 14:04:53 +00:00
|
|
|
int ret;
|
|
|
|
u8 cmd[4] = {
|
|
|
|
CMD_SST_BP,
|
|
|
|
offset >> 16,
|
|
|
|
offset >> 8,
|
|
|
|
offset,
|
|
|
|
};
|
|
|
|
|
|
|
|
debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
|
2015-12-12 06:21:57 +00:00
|
|
|
spi_w8r8(spi, CMD_READ_STATUS), buf, cmd[0], offset);
|
2013-10-02 14:04:53 +00:00
|
|
|
|
|
|
|
ret = spi_flash_cmd_write_enable(flash);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), buf, 1);
|
2013-10-02 14:04:53 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-10-30 17:46:25 +00:00
|
|
|
return spi_flash_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT);
|
2013-10-02 14:04:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
|
|
|
|
const void *buf)
|
|
|
|
{
|
2015-12-12 06:21:57 +00:00
|
|
|
struct spi_slave *spi = flash->spi;
|
2013-10-02 14:04:53 +00:00
|
|
|
size_t actual, cmd_len;
|
|
|
|
int ret;
|
|
|
|
u8 cmd[4];
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_claim_bus(spi);
|
2013-10-02 14:04:53 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("SF: Unable to claim SPI bus\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the data is not word aligned, write out leading single byte */
|
|
|
|
actual = offset % 2;
|
|
|
|
if (actual) {
|
|
|
|
ret = sst_byte_write(flash, offset, buf);
|
|
|
|
if (ret)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
offset += actual;
|
|
|
|
|
|
|
|
ret = spi_flash_cmd_write_enable(flash);
|
|
|
|
if (ret)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
cmd_len = 4;
|
|
|
|
cmd[0] = CMD_SST_AAI_WP;
|
|
|
|
cmd[1] = offset >> 16;
|
|
|
|
cmd[2] = offset >> 8;
|
|
|
|
cmd[3] = offset;
|
|
|
|
|
|
|
|
for (; actual < len - 1; actual += 2) {
|
|
|
|
debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
|
2015-12-12 06:21:57 +00:00
|
|
|
spi_w8r8(spi, CMD_READ_STATUS), buf + actual,
|
2013-10-02 14:04:53 +00:00
|
|
|
cmd[0], offset);
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_flash_cmd_write(spi, cmd, cmd_len,
|
2013-10-02 14:04:53 +00:00
|
|
|
buf + actual, 2);
|
|
|
|
if (ret) {
|
|
|
|
debug("SF: sst word program failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-30 17:46:25 +00:00
|
|
|
ret = spi_flash_wait_till_ready(flash, SPI_FLASH_PROG_TIMEOUT);
|
2013-10-02 14:04:53 +00:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
cmd_len = 1;
|
|
|
|
offset += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = spi_flash_cmd_write_disable(flash);
|
|
|
|
|
|
|
|
/* If there is a single trailing byte, write it out */
|
|
|
|
if (!ret && actual != len)
|
|
|
|
ret = sst_byte_write(flash, offset, buf + actual);
|
|
|
|
|
|
|
|
done:
|
|
|
|
debug("SF: sst: program %s %zu bytes @ 0x%zx\n",
|
|
|
|
ret ? "failure" : "success", len, offset - actual);
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
spi_release_bus(spi);
|
2013-10-02 14:04:53 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2014-12-12 14:06:13 +00:00
|
|
|
|
|
|
|
int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len,
|
|
|
|
const void *buf)
|
|
|
|
{
|
2015-12-12 06:21:57 +00:00
|
|
|
struct spi_slave *spi = flash->spi;
|
2014-12-12 14:06:13 +00:00
|
|
|
size_t actual;
|
|
|
|
int ret;
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
ret = spi_claim_bus(spi);
|
2014-12-12 14:06:13 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("SF: Unable to claim SPI bus\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (actual = 0; actual < len; actual++) {
|
|
|
|
ret = sst_byte_write(flash, offset, buf + actual);
|
|
|
|
if (ret) {
|
|
|
|
debug("SF: sst byte program failed\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = spi_flash_cmd_write_disable(flash);
|
|
|
|
|
|
|
|
debug("SF: sst: program %s %zu bytes @ 0x%zx\n",
|
|
|
|
ret ? "failure" : "success", len, offset - actual);
|
|
|
|
|
2015-12-12 06:21:57 +00:00
|
|
|
spi_release_bus(spi);
|
2014-12-12 14:06:13 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2013-10-02 14:04:53 +00:00
|
|
|
#endif
|
2015-11-05 14:43:41 +00:00
|
|
|
|
2015-11-17 18:50:53 +00:00
|
|
|
#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
|
2015-11-05 14:43:41 +00:00
|
|
|
static void stm_get_locked_range(struct spi_flash *flash, u8 sr, loff_t *ofs,
|
sf: Correct data types in stm_is_locked_sr()
The stm_is_locked_sr() function is picked from Linux kernel. For reason
unknown, the 64bit data types used by the function and present in Linux
were replaced with 32bit unsigned ones, which causes trouble.
The testcase performed was done using ST M25P80 chip.
The command used was:
=> sf protect unlock 0 0x10000
The call chain starts in stm_unlock(), which calls stm_is_locked_sr()
with negative ofs argument. This works fine in Linux, where the "ofs"
is loff_t, which is signed long long, while this fails in U-Boot, where
"ofs" is u32 (unsigned int). Because of this signedness problem, the
expression past the return statement to be incorrectly evaluated to 1,
which in turn propagates back to stm_unlock() and results in -EINVAL.
The correction is very simple, just use the correctly sized data types
with correct signedness in the function to make it work as intended.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Simon Glass <sjg@chromium.org>
Reviewed-by: Jagan Teki <jteki@openedev.com>
2016-03-11 02:20:16 +00:00
|
|
|
u64 *len)
|
2015-11-05 14:43:41 +00:00
|
|
|
{
|
|
|
|
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
|
|
|
int shift = ffs(mask) - 1;
|
|
|
|
int pow;
|
|
|
|
|
|
|
|
if (!(sr & mask)) {
|
|
|
|
/* No protection */
|
|
|
|
*ofs = 0;
|
|
|
|
*len = 0;
|
|
|
|
} else {
|
|
|
|
pow = ((sr & mask) ^ mask) >> shift;
|
|
|
|
*len = flash->size >> pow;
|
|
|
|
*ofs = flash->size - *len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 1 if the entire region is locked, 0 otherwise
|
|
|
|
*/
|
sf: Correct data types in stm_is_locked_sr()
The stm_is_locked_sr() function is picked from Linux kernel. For reason
unknown, the 64bit data types used by the function and present in Linux
were replaced with 32bit unsigned ones, which causes trouble.
The testcase performed was done using ST M25P80 chip.
The command used was:
=> sf protect unlock 0 0x10000
The call chain starts in stm_unlock(), which calls stm_is_locked_sr()
with negative ofs argument. This works fine in Linux, where the "ofs"
is loff_t, which is signed long long, while this fails in U-Boot, where
"ofs" is u32 (unsigned int). Because of this signedness problem, the
expression past the return statement to be incorrectly evaluated to 1,
which in turn propagates back to stm_unlock() and results in -EINVAL.
The correction is very simple, just use the correctly sized data types
with correct signedness in the function to make it work as intended.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Simon Glass <sjg@chromium.org>
Reviewed-by: Jagan Teki <jteki@openedev.com>
2016-03-11 02:20:16 +00:00
|
|
|
static int stm_is_locked_sr(struct spi_flash *flash, loff_t ofs, u64 len,
|
2015-11-05 14:43:41 +00:00
|
|
|
u8 sr)
|
|
|
|
{
|
|
|
|
loff_t lock_offs;
|
sf: Correct data types in stm_is_locked_sr()
The stm_is_locked_sr() function is picked from Linux kernel. For reason
unknown, the 64bit data types used by the function and present in Linux
were replaced with 32bit unsigned ones, which causes trouble.
The testcase performed was done using ST M25P80 chip.
The command used was:
=> sf protect unlock 0 0x10000
The call chain starts in stm_unlock(), which calls stm_is_locked_sr()
with negative ofs argument. This works fine in Linux, where the "ofs"
is loff_t, which is signed long long, while this fails in U-Boot, where
"ofs" is u32 (unsigned int). Because of this signedness problem, the
expression past the return statement to be incorrectly evaluated to 1,
which in turn propagates back to stm_unlock() and results in -EINVAL.
The correction is very simple, just use the correctly sized data types
with correct signedness in the function to make it work as intended.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Simon Glass <sjg@chromium.org>
Reviewed-by: Jagan Teki <jteki@openedev.com>
2016-03-11 02:20:16 +00:00
|
|
|
u64 lock_len;
|
2015-11-05 14:43:41 +00:00
|
|
|
|
|
|
|
stm_get_locked_range(flash, sr, &lock_offs, &lock_len);
|
|
|
|
|
|
|
|
return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if a region of the flash is (completely) locked. See stm_lock() for
|
|
|
|
* more info.
|
|
|
|
*
|
|
|
|
* Returns 1 if entire region is locked, 0 if any portion is unlocked, and
|
|
|
|
* negative on errors.
|
|
|
|
*/
|
2015-11-05 14:43:42 +00:00
|
|
|
int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len)
|
2015-11-05 14:43:41 +00:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
u8 sr;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
status = read_sr(flash, &sr);
|
2015-11-05 14:43:41 +00:00
|
|
|
if (status < 0)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
return stm_is_locked_sr(flash, ofs, len, sr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lock a region of the flash. Compatible with ST Micro and similar flash.
|
|
|
|
* Supports only the block protection bits BP{0,1,2} in the status register
|
|
|
|
* (SR). Does not support these features found in newer SR bitfields:
|
|
|
|
* - TB: top/bottom protect - only handle TB=0 (top protect)
|
|
|
|
* - SEC: sector/block protect - only handle SEC=0 (block protect)
|
|
|
|
* - CMP: complement protect - only support CMP=0 (range is not complemented)
|
|
|
|
*
|
|
|
|
* Sample table portion for 8MB flash (Winbond w25q64fw):
|
|
|
|
*
|
|
|
|
* SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion
|
|
|
|
* --------------------------------------------------------------------------
|
|
|
|
* X | X | 0 | 0 | 0 | NONE | NONE
|
|
|
|
* 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64
|
|
|
|
* 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32
|
|
|
|
* 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16
|
|
|
|
* 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8
|
|
|
|
* 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4
|
|
|
|
* 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2
|
|
|
|
* X | X | 1 | 1 | 1 | 8 MB | ALL
|
|
|
|
*
|
|
|
|
* Returns negative on errors, 0 on success.
|
|
|
|
*/
|
|
|
|
int stm_lock(struct spi_flash *flash, u32 ofs, size_t len)
|
|
|
|
{
|
|
|
|
u8 status_old, status_new;
|
|
|
|
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
|
|
|
u8 shift = ffs(mask) - 1, pow, val;
|
2015-11-17 19:13:33 +00:00
|
|
|
int ret;
|
2015-11-05 14:43:41 +00:00
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
ret = read_sr(flash, &status_old);
|
2015-11-17 19:13:33 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2015-11-05 14:43:41 +00:00
|
|
|
|
|
|
|
/* SPI NOR always locks to the end */
|
|
|
|
if (ofs + len != flash->size) {
|
|
|
|
/* Does combined region extend to end? */
|
|
|
|
if (!stm_is_locked_sr(flash, ofs + len, flash->size - ofs - len,
|
|
|
|
status_old))
|
|
|
|
return -EINVAL;
|
|
|
|
len = flash->size - ofs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Need smallest pow such that:
|
|
|
|
*
|
|
|
|
* 1 / (2^pow) <= (len / size)
|
|
|
|
*
|
|
|
|
* so (assuming power-of-2 size) we do:
|
|
|
|
*
|
|
|
|
* pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
|
|
|
|
*/
|
|
|
|
pow = ilog2(flash->size) - ilog2(len);
|
|
|
|
val = mask - (pow << shift);
|
|
|
|
if (val & ~mask)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Don't "lock" with no region! */
|
|
|
|
if (!(val & mask))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
status_new = (status_old & ~mask) | val;
|
|
|
|
|
|
|
|
/* Only modify protection if it will not unlock other areas */
|
|
|
|
if ((status_new & mask) <= (status_old & mask))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
write_sr(flash, status_new);
|
2015-11-05 14:43:41 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlock a region of the flash. See stm_lock() for more info
|
|
|
|
*
|
|
|
|
* Returns negative on errors, 0 on success.
|
|
|
|
*/
|
|
|
|
int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len)
|
|
|
|
{
|
|
|
|
uint8_t status_old, status_new;
|
|
|
|
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
|
|
|
|
u8 shift = ffs(mask) - 1, pow, val;
|
2015-11-17 19:13:33 +00:00
|
|
|
int ret;
|
2015-11-05 14:43:41 +00:00
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
ret = read_sr(flash, &status_old);
|
2015-11-17 19:13:33 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2015-11-05 14:43:41 +00:00
|
|
|
|
|
|
|
/* Cannot unlock; would unlock larger region than requested */
|
2016-01-06 00:24:39 +00:00
|
|
|
if (stm_is_locked_sr(flash, ofs - flash->erase_size, flash->erase_size,
|
|
|
|
status_old))
|
2015-11-05 14:43:41 +00:00
|
|
|
return -EINVAL;
|
|
|
|
/*
|
|
|
|
* Need largest pow such that:
|
|
|
|
*
|
|
|
|
* 1 / (2^pow) >= (len / size)
|
|
|
|
*
|
|
|
|
* so (assuming power-of-2 size) we do:
|
|
|
|
*
|
|
|
|
* pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
|
|
|
|
*/
|
|
|
|
pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len));
|
|
|
|
if (ofs + len == flash->size) {
|
|
|
|
val = 0; /* fully unlocked */
|
|
|
|
} else {
|
|
|
|
val = mask - (pow << shift);
|
|
|
|
/* Some power-of-two sizes are not supported */
|
|
|
|
if (val & ~mask)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_new = (status_old & ~mask) | val;
|
|
|
|
|
|
|
|
/* Only modify protection if it will not lock other areas */
|
|
|
|
if ((status_new & mask) >= (status_old & mask))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
write_sr(flash, status_new);
|
2015-11-05 14:43:41 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-11-17 18:50:53 +00:00
|
|
|
#endif
|
2015-12-11 16:06:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_SPI_FLASH_MACRONIX
|
2015-12-13 17:34:46 +00:00
|
|
|
static int macronix_quad_enable(struct spi_flash *flash)
|
2015-12-11 16:06:34 +00:00
|
|
|
{
|
|
|
|
u8 qeb_status;
|
|
|
|
int ret;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
ret = read_sr(flash, &qeb_status);
|
2015-12-11 16:06:34 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2015-12-15 07:12:02 +00:00
|
|
|
if (qeb_status & STATUS_QEB_MXIC)
|
|
|
|
return 0;
|
|
|
|
|
2015-12-16 08:18:08 +00:00
|
|
|
ret = write_sr(flash, qeb_status | STATUS_QEB_MXIC);
|
2015-12-15 07:12:02 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* read SR and check it */
|
|
|
|
ret = read_sr(flash, &qeb_status);
|
|
|
|
if (!(ret >= 0 && (qeb_status & STATUS_QEB_MXIC))) {
|
|
|
|
printf("SF: Macronix SR Quad bit not clear\n");
|
|
|
|
return -EINVAL;
|
2015-12-11 16:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
|
2015-12-13 17:34:46 +00:00
|
|
|
static int spansion_quad_enable(struct spi_flash *flash)
|
2015-12-11 16:06:34 +00:00
|
|
|
{
|
|
|
|
u8 qeb_status;
|
|
|
|
int ret;
|
|
|
|
|
2015-09-29 16:59:33 +00:00
|
|
|
ret = read_cr(flash, &qeb_status);
|
2015-12-11 16:06:34 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2015-12-15 06:58:39 +00:00
|
|
|
if (qeb_status & STATUS_QEB_WINSPAN)
|
|
|
|
return 0;
|
|
|
|
|
2015-12-16 08:18:08 +00:00
|
|
|
ret = write_cr(flash, qeb_status | STATUS_QEB_WINSPAN);
|
2015-12-15 06:58:39 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* read CR and check it */
|
|
|
|
ret = read_cr(flash, &qeb_status);
|
|
|
|
if (!(ret >= 0 && (qeb_status & STATUS_QEB_WINSPAN))) {
|
|
|
|
printf("SF: Spansion CR Quad bit not clear\n");
|
|
|
|
return -EINVAL;
|
2015-12-11 16:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-10-30 17:46:10 +00:00
|
|
|
static const struct spi_flash_info *spi_flash_read_id(struct spi_flash *flash)
|
2015-12-11 16:06:34 +00:00
|
|
|
{
|
2016-10-30 17:46:10 +00:00
|
|
|
int tmp;
|
2016-10-30 17:46:16 +00:00
|
|
|
u8 id[SPI_FLASH_MAX_ID_LEN];
|
2016-10-30 17:46:10 +00:00
|
|
|
const struct spi_flash_info *info;
|
|
|
|
|
2016-10-30 17:46:16 +00:00
|
|
|
tmp = spi_flash_cmd(flash->spi, CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
|
2016-10-30 17:46:10 +00:00
|
|
|
if (tmp < 0) {
|
|
|
|
printf("SF: error %d reading JEDEC ID\n", tmp);
|
|
|
|
return ERR_PTR(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
info = spi_flash_ids;
|
|
|
|
for (; info->name != NULL; info++) {
|
|
|
|
if (info->id_len) {
|
|
|
|
if (!memcmp(info->id, id, info->id_len))
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n",
|
|
|
|
id[0], id[1], id[2]);
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_quad_mode(struct spi_flash *flash,
|
|
|
|
const struct spi_flash_info *info)
|
|
|
|
{
|
|
|
|
switch (JEDEC_MFR(info)) {
|
2015-12-11 16:06:34 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_MACRONIX
|
|
|
|
case SPI_FLASH_CFI_MFR_MACRONIX:
|
2015-12-13 17:34:46 +00:00
|
|
|
return macronix_quad_enable(flash);
|
2015-12-11 16:06:34 +00:00
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
|
|
|
|
case SPI_FLASH_CFI_MFR_SPANSION:
|
|
|
|
case SPI_FLASH_CFI_MFR_WINBOND:
|
2015-12-13 17:34:46 +00:00
|
|
|
return spansion_quad_enable(flash);
|
2015-12-11 16:06:34 +00:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SPI_FLASH_STMICRO
|
|
|
|
case SPI_FLASH_CFI_MFR_STMICRO:
|
2016-12-15 16:45:39 +00:00
|
|
|
debug("SF: QEB is volatile for %02x flash\n", JEDEC_MFR(info));
|
|
|
|
return 0;
|
2015-12-11 16:06:34 +00:00
|
|
|
#endif
|
|
|
|
default:
|
2016-10-30 17:46:10 +00:00
|
|
|
printf("SF: Need set QEB func for %02x flash\n",
|
|
|
|
JEDEC_MFR(info));
|
2015-12-11 16:06:34 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
2017-05-19 02:09:57 +00:00
|
|
|
int spi_flash_decode_fdt(struct spi_flash *flash)
|
2015-12-11 16:06:34 +00:00
|
|
|
{
|
2016-01-22 02:43:54 +00:00
|
|
|
#ifdef CONFIG_DM_SPI_FLASH
|
2015-12-11 16:06:34 +00:00
|
|
|
fdt_addr_t addr;
|
|
|
|
fdt_size_t size;
|
|
|
|
|
2017-05-19 02:09:57 +00:00
|
|
|
addr = dev_read_addr_size(flash->dev, "memory-map", &size);
|
2015-12-11 16:06:34 +00:00
|
|
|
if (addr == FDT_ADDR_T_NONE) {
|
|
|
|
debug("%s: Cannot decode address\n", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-09 15:03:39 +00:00
|
|
|
if (flash->size > size) {
|
2015-12-11 16:06:34 +00:00
|
|
|
debug("%s: Memory map must cover entire device\n", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
flash->memory_map = map_sysmem(addr, size);
|
2016-01-22 02:43:54 +00:00
|
|
|
#endif
|
2015-12-11 16:06:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
|
|
|
|
|
2015-12-06 16:03:32 +00:00
|
|
|
int spi_flash_scan(struct spi_flash *flash)
|
2015-12-11 16:06:34 +00:00
|
|
|
{
|
2015-12-06 16:03:32 +00:00
|
|
|
struct spi_slave *spi = flash->spi;
|
2016-10-30 17:46:10 +00:00
|
|
|
const struct spi_flash_info *info = NULL;
|
2016-12-05 18:09:10 +00:00
|
|
|
int ret;
|
2015-12-11 16:06:34 +00:00
|
|
|
|
2016-10-30 17:46:10 +00:00
|
|
|
info = spi_flash_read_id(flash);
|
|
|
|
if (IS_ERR_OR_NULL(info))
|
|
|
|
return -ENOENT;
|
2015-12-11 16:06:34 +00:00
|
|
|
|
2017-07-23 14:44:37 +00:00
|
|
|
/*
|
|
|
|
* Flash powers up read-only, so clear BP# bits.
|
|
|
|
*
|
|
|
|
* Note on some flash (like Macronix), QE (quad enable) bit is in the
|
|
|
|
* same status register as BP# bits, and we need preserve its original
|
|
|
|
* value during a reboot cycle as this is required by some platforms
|
|
|
|
* (like Intel ICH SPI controller working under descriptor mode).
|
|
|
|
*/
|
2016-10-30 17:46:10 +00:00
|
|
|
if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_ATMEL ||
|
2017-07-23 14:44:37 +00:00
|
|
|
(JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST) ||
|
|
|
|
(JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX)) {
|
|
|
|
u8 sr = 0;
|
|
|
|
|
|
|
|
if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_MACRONIX) {
|
|
|
|
read_sr(flash, &sr);
|
|
|
|
sr &= STATUS_QEB_MXIC;
|
|
|
|
}
|
|
|
|
write_sr(flash, sr);
|
|
|
|
}
|
2015-12-11 16:06:34 +00:00
|
|
|
|
2016-10-30 17:46:10 +00:00
|
|
|
flash->name = info->name;
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->memory_map = spi->memory_map;
|
|
|
|
|
2016-10-30 17:46:10 +00:00
|
|
|
if (info->flags & SST_WR)
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->flags |= SNOR_F_SST_WR;
|
|
|
|
|
|
|
|
#ifndef CONFIG_DM_SPI_FLASH
|
|
|
|
flash->write = spi_flash_cmd_write_ops;
|
|
|
|
#if defined(CONFIG_SPI_FLASH_SST)
|
|
|
|
if (flash->flags & SNOR_F_SST_WR) {
|
2015-12-13 14:42:45 +00:00
|
|
|
if (spi->mode & SPI_TX_BYTE)
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->write = sst_write_bp;
|
|
|
|
else
|
|
|
|
flash->write = sst_write_wp;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
flash->erase = spi_flash_cmd_erase_ops;
|
|
|
|
flash->read = spi_flash_cmd_read_ops;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
|
2016-10-30 17:46:11 +00:00
|
|
|
/* NOR protection support for STmicro/Micron chips and similar */
|
|
|
|
if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_STMICRO ||
|
|
|
|
JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST) {
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->flash_lock = stm_lock;
|
|
|
|
flash->flash_unlock = stm_unlock;
|
|
|
|
flash->flash_is_locked = stm_is_locked;
|
|
|
|
}
|
2016-10-30 17:46:11 +00:00
|
|
|
#endif
|
2015-12-11 16:06:34 +00:00
|
|
|
|
2018-04-10 11:40:44 +00:00
|
|
|
/* sst26wf series block protection implementation differs from other series */
|
|
|
|
#if defined(CONFIG_SPI_FLASH_SST)
|
|
|
|
if (JEDEC_MFR(info) == SPI_FLASH_CFI_MFR_SST && info->id[1] == 0x26) {
|
|
|
|
flash->flash_lock = sst26_lock;
|
|
|
|
flash->flash_unlock = sst26_unlock;
|
|
|
|
flash->flash_is_locked = sst26_is_locked;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-12-11 16:06:34 +00:00
|
|
|
/* Compute the flash size */
|
|
|
|
flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0;
|
2016-10-30 17:46:10 +00:00
|
|
|
flash->page_size = info->page_size;
|
2015-12-11 16:06:34 +00:00
|
|
|
/*
|
2018-05-07 10:31:47 +00:00
|
|
|
* The Spansion S25FS512S, S25FL032P and S25FL064P have 256b pages,
|
|
|
|
* yet use the 0x4d00 Extended JEDEC code. The rest of the Spansion
|
|
|
|
* flashes with the 0x4d00 Extended JEDEC code have 512b pages.
|
|
|
|
* All of the others have 256b pages.
|
2015-12-11 16:06:34 +00:00
|
|
|
*/
|
2016-10-30 17:46:10 +00:00
|
|
|
if (JEDEC_EXT(info) == 0x4d00) {
|
|
|
|
if ((JEDEC_ID(info) != 0x0215) &&
|
2018-05-07 10:31:47 +00:00
|
|
|
(JEDEC_ID(info) != 0x0216) &&
|
|
|
|
(JEDEC_ID(info) != 0x0220))
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->page_size = 512;
|
|
|
|
}
|
|
|
|
flash->page_size <<= flash->shift;
|
2016-10-30 17:46:10 +00:00
|
|
|
flash->sector_size = info->sector_size << flash->shift;
|
2016-10-30 17:46:15 +00:00
|
|
|
flash->size = flash->sector_size * info->n_sectors << flash->shift;
|
2015-12-11 16:06:34 +00:00
|
|
|
#ifdef CONFIG_SF_DUAL_FLASH
|
|
|
|
if (flash->dual_flash & SF_DUAL_STACKED_FLASH)
|
|
|
|
flash->size <<= 1;
|
|
|
|
#endif
|
|
|
|
|
2016-08-08 11:53:56 +00:00
|
|
|
#ifdef CONFIG_SPI_FLASH_USE_4K_SECTORS
|
2015-12-11 16:06:34 +00:00
|
|
|
/* Compute erase sector and command */
|
2016-10-30 17:46:10 +00:00
|
|
|
if (info->flags & SECT_4K) {
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->erase_cmd = CMD_ERASE_4K;
|
|
|
|
flash->erase_size = 4096 << flash->shift;
|
2016-08-08 11:53:56 +00:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->erase_cmd = CMD_ERASE_64K;
|
|
|
|
flash->erase_size = flash->sector_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now erase size becomes valid sector size */
|
|
|
|
flash->sector_size = flash->erase_size;
|
|
|
|
|
2016-08-08 11:20:45 +00:00
|
|
|
/* Look for read commands */
|
|
|
|
flash->read_cmd = CMD_READ_ARRAY_FAST;
|
2016-08-08 11:42:12 +00:00
|
|
|
if (spi->mode & SPI_RX_SLOW)
|
2016-08-08 11:20:45 +00:00
|
|
|
flash->read_cmd = CMD_READ_ARRAY_SLOW;
|
2016-10-30 17:46:10 +00:00
|
|
|
else if (spi->mode & SPI_RX_QUAD && info->flags & RD_QUAD)
|
2016-08-08 11:20:45 +00:00
|
|
|
flash->read_cmd = CMD_READ_QUAD_OUTPUT_FAST;
|
2016-10-30 17:46:10 +00:00
|
|
|
else if (spi->mode & SPI_RX_DUAL && info->flags & RD_DUAL)
|
2016-08-08 11:20:45 +00:00
|
|
|
flash->read_cmd = CMD_READ_DUAL_OUTPUT_FAST;
|
2015-12-11 16:06:34 +00:00
|
|
|
|
2016-08-08 11:20:45 +00:00
|
|
|
/* Look for write commands */
|
2016-10-30 17:46:10 +00:00
|
|
|
if (info->flags & WR_QPP && spi->mode & SPI_TX_QUAD)
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->write_cmd = CMD_QUAD_PAGE_PROGRAM;
|
|
|
|
else
|
|
|
|
/* Go for default supported write cmd */
|
|
|
|
flash->write_cmd = CMD_PAGE_PROGRAM;
|
|
|
|
|
|
|
|
/* Set the quad enable bit - only for quad commands */
|
|
|
|
if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) ||
|
|
|
|
(flash->read_cmd == CMD_READ_QUAD_IO_FAST) ||
|
|
|
|
(flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) {
|
2016-10-30 17:46:10 +00:00
|
|
|
ret = set_quad_mode(flash, info);
|
2015-12-11 16:06:34 +00:00
|
|
|
if (ret) {
|
2016-10-30 17:46:10 +00:00
|
|
|
debug("SF: Fail to set QEB for %02x\n",
|
|
|
|
JEDEC_MFR(info));
|
2015-12-11 16:06:34 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read dummy_byte: dummy byte is determined based on the
|
|
|
|
* dummy cycles of a particular command.
|
|
|
|
* Fast commands - dummy_byte = dummy_cycles/8
|
|
|
|
* I/O commands- dummy_byte = (dummy_cycles * no.of lines)/8
|
|
|
|
* For I/O commands except cmd[0] everything goes on no.of lines
|
|
|
|
* based on particular command but incase of fast commands except
|
|
|
|
* data all go on single line irrespective of command.
|
|
|
|
*/
|
|
|
|
switch (flash->read_cmd) {
|
|
|
|
case CMD_READ_QUAD_IO_FAST:
|
|
|
|
flash->dummy_byte = 2;
|
|
|
|
break;
|
|
|
|
case CMD_READ_ARRAY_SLOW:
|
|
|
|
flash->dummy_byte = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
flash->dummy_byte = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SPI_FLASH_STMICRO
|
2016-10-30 17:46:10 +00:00
|
|
|
if (info->flags & E_FSR)
|
2015-12-11 16:06:34 +00:00
|
|
|
flash->flags |= SNOR_F_USE_FSR;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Configure the BAR - discover bank cmds and read current bank */
|
|
|
|
#ifdef CONFIG_SPI_FLASH_BAR
|
2016-10-30 17:46:25 +00:00
|
|
|
ret = read_bar(flash, info);
|
2015-12-11 16:06:34 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
#endif
|
|
|
|
|
2016-11-13 21:22:01 +00:00
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
2017-05-19 02:09:57 +00:00
|
|
|
ret = spi_flash_decode_fdt(flash);
|
2015-12-11 16:06:34 +00:00
|
|
|
if (ret) {
|
|
|
|
debug("SF: FDT decode error\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef CONFIG_SPL_BUILD
|
|
|
|
printf("SF: Detected %s with page size ", flash->name);
|
|
|
|
print_size(flash->page_size, ", erase size ");
|
|
|
|
print_size(flash->erase_size, ", total ");
|
|
|
|
print_size(flash->size, "");
|
|
|
|
if (flash->memory_map)
|
|
|
|
printf(", mapped at %p", flash->memory_map);
|
|
|
|
puts("\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef CONFIG_SPI_FLASH_BAR
|
|
|
|
if (((flash->dual_flash == SF_SINGLE_FLASH) &&
|
|
|
|
(flash->size > SPI_FLASH_16MB_BOUN)) ||
|
|
|
|
((flash->dual_flash > SF_SINGLE_FLASH) &&
|
|
|
|
(flash->size > SPI_FLASH_16MB_BOUN << 1))) {
|
|
|
|
puts("SF: Warning - Only lower 16MiB accessible,");
|
|
|
|
puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-12-05 18:09:10 +00:00
|
|
|
return 0;
|
2015-12-11 16:06:34 +00:00
|
|
|
}
|