avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
131
u-boot/drivers/mtd/spi/Kconfig
Normal file
131
u-boot/drivers/mtd/spi/Kconfig
Normal file
@@ -0,0 +1,131 @@
|
||||
menu "SPI Flash Support"
|
||||
|
||||
config DM_SPI_FLASH
|
||||
bool "Enable Driver Model for SPI flash"
|
||||
depends on DM && DM_SPI
|
||||
help
|
||||
Enable driver model for SPI flash. This SPI flash interface
|
||||
(spi_flash_probe(), spi_flash_write(), etc.) is then
|
||||
implemented by the SPI flash uclass. There is one standard
|
||||
SPI flash driver which knows how to probe most chips
|
||||
supported by U-Boot. The uclass interface is defined in
|
||||
include/spi_flash.h, but is currently fully compatible
|
||||
with the old interface to avoid confusion and duplication
|
||||
during the transition parent. SPI and SPI flash must be
|
||||
enabled together (it is not possible to use driver model
|
||||
for one and not the other).
|
||||
|
||||
config SPI_FLASH_SANDBOX
|
||||
bool "Support sandbox SPI flash device"
|
||||
depends on SANDBOX && DM_SPI_FLASH
|
||||
help
|
||||
Since sandbox cannot access real devices, an emulation mechanism is
|
||||
provided instead. Drivers can be connected up to the sandbox SPI
|
||||
bus (see CONFIG_SANDBOX_SPI) and SPI traffic will be routed to this
|
||||
device. Typically the contents of the emulated SPI flash device is
|
||||
stored in a file on the host filesystem.
|
||||
|
||||
config SPI_FLASH
|
||||
bool "Legacy SPI Flash Interface support"
|
||||
help
|
||||
Enable the legacy SPI flash support. This will include basic
|
||||
standard support for things like probing, read / write, and
|
||||
erasing through cmd_sf interface.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config SPI_FLASH_BAR
|
||||
bool "SPI flash Bank/Extended address register support"
|
||||
depends on SPI_FLASH
|
||||
help
|
||||
Enable the SPI flash Bank/Extended address register support.
|
||||
Bank/Extended address registers are used to access the flash
|
||||
which has size > 16MiB in 3-byte addressing.
|
||||
|
||||
if SPI_FLASH
|
||||
|
||||
config SPI_FLASH_ATMEL
|
||||
bool "Atmel SPI flash support"
|
||||
help
|
||||
Add support for various Atmel SPI flash chips (AT45xxx and AT25xxx)
|
||||
|
||||
config SPI_FLASH_EON
|
||||
bool "EON SPI flash support"
|
||||
help
|
||||
Add support for various EON SPI flash chips (EN25xxx)
|
||||
|
||||
config SPI_FLASH_GIGADEVICE
|
||||
bool "GigaDevice SPI flash support"
|
||||
help
|
||||
Add support for various GigaDevice SPI flash chips (GD25xxx)
|
||||
|
||||
config SPI_FLASH_MACRONIX
|
||||
bool "Macronix SPI flash support"
|
||||
help
|
||||
Add support for various Macronix SPI flash chips (MX25Lxxx)
|
||||
|
||||
config SPI_FLASH_SPANSION
|
||||
bool "Spansion SPI flash support"
|
||||
help
|
||||
Add support for various Spansion SPI flash chips (S25FLxxx)
|
||||
|
||||
config SPI_FLASH_STMICRO
|
||||
bool "STMicro SPI flash support"
|
||||
help
|
||||
Add support for various STMicro SPI flash chips (M25Pxxx and N25Qxxx)
|
||||
|
||||
config SPI_FLASH_SST
|
||||
bool "SST SPI flash support"
|
||||
help
|
||||
Add support for various SST SPI flash chips (SST25xxx)
|
||||
|
||||
config SPI_FLASH_WINBOND
|
||||
bool "Winbond SPI flash support"
|
||||
help
|
||||
Add support for various Winbond SPI flash chips (W25xxx)
|
||||
|
||||
endif
|
||||
|
||||
config SPI_FLASH_USE_4K_SECTORS
|
||||
bool "Use small 4096 B erase sectors"
|
||||
depends on SPI_FLASH
|
||||
default y
|
||||
help
|
||||
Many flash memories support erasing small (4096 B) sectors. Depending
|
||||
on the usage this feature may provide performance gain in comparison
|
||||
to erasing whole blocks (32/64 KiB).
|
||||
Changing a small part of the flash's contents is usually faster with
|
||||
small sectors. On the other hand erasing should be faster when using
|
||||
64 KiB block instead of 16 × 4 KiB sectors.
|
||||
|
||||
Please note that some tools/drivers/filesystems may not work with
|
||||
4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
|
||||
|
||||
config SPI_FLASH_DATAFLASH
|
||||
bool "AT45xxx DataFlash support"
|
||||
depends on SPI_FLASH && DM_SPI_FLASH
|
||||
help
|
||||
Enable the access for SPI-flash-based AT45xxx DataFlash chips.
|
||||
DataFlash is a kind of SPI flash. Most AT45 chips have two buffers
|
||||
in each chip, which may be used for double buffered I/O; but this
|
||||
driver doesn't (yet) use these for any kind of i/o overlap or prefetching.
|
||||
|
||||
Sometimes DataFlash is packaged in MMC-format cards, although the
|
||||
MMC stack can't (yet?) distinguish between MMC and DataFlash
|
||||
protocols during enumeration.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
config SPI_FLASH_MTD
|
||||
bool "SPI Flash MTD support"
|
||||
depends on SPI_FLASH
|
||||
help
|
||||
Enable the MTD support for spi flash layer, this adapter is for
|
||||
translating mtd_read/mtd_write commands into spi_flash_read/write
|
||||
commands. It is not intended to use it within sf_cmd or the SPI
|
||||
flash subsystem. Such an adapter is needed for subsystems like
|
||||
UBI which can only operate on top of the MTD layer.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
endmenu # menu "SPI Flash Support"
|
||||
18
u-boot/drivers/mtd/spi/Makefile
Normal file
18
u-boot/drivers/mtd/spi/Makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# (C) Copyright 2006
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DM_SPI_FLASH) += sf-uclass.o
|
||||
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o
|
||||
obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o sf_params.o sf.o
|
||||
obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o
|
||||
obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o
|
||||
obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o
|
||||
90
u-boot/drivers/mtd/spi/fsl_espi_spl.c
Normal file
90
u-boot/drivers/mtd/spi/fsl_espi_spl.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <spi_flash.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define ESPI_BOOT_IMAGE_SIZE 0x48
|
||||
#define ESPI_BOOT_IMAGE_ADDR 0x50
|
||||
#define CONFIG_CFG_DATA_SECTOR 0
|
||||
|
||||
void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst)
|
||||
{
|
||||
struct spi_flash *flash;
|
||||
|
||||
flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
|
||||
CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
|
||||
if (flash == NULL) {
|
||||
puts("\nspi_flash_probe failed");
|
||||
hang();
|
||||
}
|
||||
|
||||
spi_flash_read(flash, offs, size, vdst);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main entry for SPI booting. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from SPI into SDRAM and starts it from there.
|
||||
*/
|
||||
void spi_boot(void)
|
||||
{
|
||||
void (*uboot)(void) __noreturn;
|
||||
u32 offset, code_len, copy_len = 0;
|
||||
#ifndef CONFIG_FSL_CORENET
|
||||
unsigned char *buf = NULL;
|
||||
#endif
|
||||
struct spi_flash *flash;
|
||||
|
||||
flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
|
||||
CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
|
||||
if (flash == NULL) {
|
||||
puts("\nspi_flash_probe failed");
|
||||
hang();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
offset = CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS;
|
||||
code_len = CONFIG_SYS_SPI_FLASH_U_BOOT_SIZE;
|
||||
#else
|
||||
/*
|
||||
* Load U-Boot image from SPI flash into RAM
|
||||
*/
|
||||
buf = malloc(flash->page_size);
|
||||
if (buf == NULL) {
|
||||
puts("\nmalloc failed");
|
||||
hang();
|
||||
}
|
||||
memset(buf, 0, flash->page_size);
|
||||
|
||||
spi_flash_read(flash, CONFIG_CFG_DATA_SECTOR,
|
||||
flash->page_size, (void *)buf);
|
||||
offset = *(u32 *)(buf + ESPI_BOOT_IMAGE_ADDR);
|
||||
/* Skip spl code */
|
||||
offset += CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS;
|
||||
/* Get the code size from offset 0x48 */
|
||||
code_len = *(u32 *)(buf + ESPI_BOOT_IMAGE_SIZE);
|
||||
/* Skip spl code */
|
||||
code_len = code_len - CONFIG_SPL_MAX_SIZE;
|
||||
#endif
|
||||
/* copy code to DDR */
|
||||
printf("Loading second stage boot loader ");
|
||||
while (copy_len <= code_len) {
|
||||
spi_flash_read(flash, offset + copy_len, 0x2000,
|
||||
(void *)(CONFIG_SYS_SPI_FLASH_U_BOOT_DST
|
||||
+ copy_len));
|
||||
copy_len = copy_len + 0x2000;
|
||||
putc('.');
|
||||
}
|
||||
|
||||
/*
|
||||
* Jump to U-Boot image
|
||||
*/
|
||||
flush_cache(CONFIG_SYS_SPI_FLASH_U_BOOT_DST, code_len);
|
||||
uboot = (void *)CONFIG_SYS_SPI_FLASH_U_BOOT_START;
|
||||
(*uboot)();
|
||||
}
|
||||
703
u-boot/drivers/mtd/spi/sandbox.c
Normal file
703
u-boot/drivers/mtd/spi/sandbox.c
Normal file
@@ -0,0 +1,703 @@
|
||||
/*
|
||||
* Simulate a SPI flash
|
||||
*
|
||||
* Copyright (c) 2011-2013 The Chromium OS Authors.
|
||||
* See file CREDITS for list of people who contributed to this
|
||||
* project.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <os.h>
|
||||
|
||||
#include <spi_flash.h>
|
||||
#include "sf_internal.h"
|
||||
|
||||
#include <asm/getopt.h>
|
||||
#include <asm/spi.h>
|
||||
#include <asm/state.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* The different states that our SPI flash transitions between.
|
||||
* We need to keep track of this across multiple xfer calls since
|
||||
* the SPI bus could possibly call down into us multiple times.
|
||||
*/
|
||||
enum sandbox_sf_state {
|
||||
SF_CMD, /* default state -- we're awaiting a command */
|
||||
SF_ID, /* read the flash's (jedec) ID code */
|
||||
SF_ADDR, /* processing the offset in the flash to read/etc... */
|
||||
SF_READ, /* reading data from the flash */
|
||||
SF_WRITE, /* writing data to the flash, i.e. page programming */
|
||||
SF_ERASE, /* erase the flash */
|
||||
SF_READ_STATUS, /* read the flash's status register */
|
||||
SF_READ_STATUS1, /* read the flash's status register upper 8 bits*/
|
||||
SF_WRITE_STATUS, /* write the flash's status register */
|
||||
};
|
||||
|
||||
static const char *sandbox_sf_state_name(enum sandbox_sf_state state)
|
||||
{
|
||||
static const char * const states[] = {
|
||||
"CMD", "ID", "ADDR", "READ", "WRITE", "ERASE", "READ_STATUS",
|
||||
"READ_STATUS1", "WRITE_STATUS",
|
||||
};
|
||||
return states[state];
|
||||
}
|
||||
|
||||
/* Bits for the status register */
|
||||
#define STAT_WIP (1 << 0)
|
||||
#define STAT_WEL (1 << 1)
|
||||
|
||||
/* Assume all SPI flashes have 3 byte addresses since they do atm */
|
||||
#define SF_ADDR_LEN 3
|
||||
|
||||
#define IDCODE_LEN 3
|
||||
|
||||
/* Used to quickly bulk erase backing store */
|
||||
static u8 sandbox_sf_0xff[0x1000];
|
||||
|
||||
/* Internal state data for each SPI flash */
|
||||
struct sandbox_spi_flash {
|
||||
unsigned int cs; /* Chip select we are attached to */
|
||||
/*
|
||||
* As we receive data over the SPI bus, our flash transitions
|
||||
* between states. For example, we start off in the SF_CMD
|
||||
* state where the first byte tells us what operation to perform
|
||||
* (such as read or write the flash). But the operation itself
|
||||
* can go through a few states such as first reading in the
|
||||
* offset in the flash to perform the requested operation.
|
||||
* Thus "state" stores the exact state that our machine is in
|
||||
* while "cmd" stores the overall command we're processing.
|
||||
*/
|
||||
enum sandbox_sf_state state;
|
||||
uint cmd;
|
||||
/* Erase size of current erase command */
|
||||
uint erase_size;
|
||||
/* Current position in the flash; used when reading/writing/etc... */
|
||||
uint off;
|
||||
/* How many address bytes we've consumed */
|
||||
uint addr_bytes, pad_addr_bytes;
|
||||
/* The current flash status (see STAT_XXX defines above) */
|
||||
u16 status;
|
||||
/* Data describing the flash we're emulating */
|
||||
const struct spi_flash_params *data;
|
||||
/* The file on disk to serv up data from */
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct sandbox_spi_flash_plat_data {
|
||||
const char *filename;
|
||||
const char *device_name;
|
||||
int bus;
|
||||
int cs;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a very strange probe function. If it has platform data (which may
|
||||
* have come from the device tree) then this function gets the filename and
|
||||
* device type from there. Failing that it looks at the command line
|
||||
* parameter.
|
||||
*/
|
||||
static int sandbox_sf_probe(struct udevice *dev)
|
||||
{
|
||||
/* spec = idcode:file */
|
||||
struct sandbox_spi_flash *sbsf = dev_get_priv(dev);
|
||||
const char *file;
|
||||
size_t len, idname_len;
|
||||
const struct spi_flash_params *data;
|
||||
struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev);
|
||||
struct sandbox_state *state = state_get_current();
|
||||
struct udevice *bus = dev->parent;
|
||||
const char *spec = NULL;
|
||||
int ret = 0;
|
||||
int cs = -1;
|
||||
int i;
|
||||
|
||||
debug("%s: bus %d, looking for emul=%p: ", __func__, bus->seq, dev);
|
||||
if (bus->seq >= 0 && bus->seq < CONFIG_SANDBOX_SPI_MAX_BUS) {
|
||||
for (i = 0; i < CONFIG_SANDBOX_SPI_MAX_CS; i++) {
|
||||
if (state->spi[bus->seq][i].emul == dev)
|
||||
cs = i;
|
||||
}
|
||||
}
|
||||
if (cs == -1) {
|
||||
printf("Error: Unknown chip select for device '%s'\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
debug("found at cs %d\n", cs);
|
||||
|
||||
if (!pdata->filename) {
|
||||
struct sandbox_state *state = state_get_current();
|
||||
|
||||
assert(bus->seq != -1);
|
||||
if (bus->seq < CONFIG_SANDBOX_SPI_MAX_BUS)
|
||||
spec = state->spi[bus->seq][cs].spec;
|
||||
if (!spec) {
|
||||
debug("%s: No spec found for bus %d, cs %d\n",
|
||||
__func__, bus->seq, cs);
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
file = strchr(spec, ':');
|
||||
if (!file) {
|
||||
printf("%s: unable to parse file\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
idname_len = file - spec;
|
||||
pdata->filename = file + 1;
|
||||
pdata->device_name = spec;
|
||||
++file;
|
||||
} else {
|
||||
spec = strchr(pdata->device_name, ',');
|
||||
if (spec)
|
||||
spec++;
|
||||
else
|
||||
spec = pdata->device_name;
|
||||
idname_len = strlen(spec);
|
||||
}
|
||||
debug("%s: device='%s'\n", __func__, spec);
|
||||
|
||||
for (data = spi_flash_params_table; data->name; data++) {
|
||||
len = strlen(data->name);
|
||||
if (idname_len != len)
|
||||
continue;
|
||||
if (!strncasecmp(spec, data->name, len))
|
||||
break;
|
||||
}
|
||||
if (!data->name) {
|
||||
printf("%s: unknown flash '%*s'\n", __func__, (int)idname_len,
|
||||
spec);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sandbox_sf_0xff[0] == 0x00)
|
||||
memset(sandbox_sf_0xff, 0xff, sizeof(sandbox_sf_0xff));
|
||||
|
||||
sbsf->fd = os_open(pdata->filename, 02);
|
||||
if (sbsf->fd == -1) {
|
||||
printf("%s: unable to open file '%s'\n", __func__,
|
||||
pdata->filename);
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
sbsf->data = data;
|
||||
sbsf->cs = cs;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
debug("%s: Got error %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sandbox_sf_remove(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_spi_flash *sbsf = dev_get_priv(dev);
|
||||
|
||||
os_close(sbsf->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sandbox_sf_cs_activate(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_spi_flash *sbsf = dev_get_priv(dev);
|
||||
|
||||
debug("sandbox_sf: CS activated; state is fresh!\n");
|
||||
|
||||
/* CS is asserted, so reset state */
|
||||
sbsf->off = 0;
|
||||
sbsf->addr_bytes = 0;
|
||||
sbsf->pad_addr_bytes = 0;
|
||||
sbsf->state = SF_CMD;
|
||||
sbsf->cmd = SF_CMD;
|
||||
}
|
||||
|
||||
static void sandbox_sf_cs_deactivate(struct udevice *dev)
|
||||
{
|
||||
debug("sandbox_sf: CS deactivated; cmd done processing!\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* There are times when the data lines are allowed to tristate. What
|
||||
* is actually sensed on the line depends on the hardware. It could
|
||||
* always be 0xFF/0x00 (if there are pull ups/downs), or things could
|
||||
* float and so we'd get garbage back. This func encapsulates that
|
||||
* scenario so we can worry about the details here.
|
||||
*/
|
||||
static void sandbox_spi_tristate(u8 *buf, uint len)
|
||||
{
|
||||
/* XXX: make this into a user config option ? */
|
||||
memset(buf, 0xff, len);
|
||||
}
|
||||
|
||||
/* Figure out what command this stream is telling us to do */
|
||||
static int sandbox_sf_process_cmd(struct sandbox_spi_flash *sbsf, const u8 *rx,
|
||||
u8 *tx)
|
||||
{
|
||||
enum sandbox_sf_state oldstate = sbsf->state;
|
||||
|
||||
/* We need to output a byte for the cmd byte we just ate */
|
||||
if (tx)
|
||||
sandbox_spi_tristate(tx, 1);
|
||||
|
||||
sbsf->cmd = rx[0];
|
||||
switch (sbsf->cmd) {
|
||||
case CMD_READ_ID:
|
||||
sbsf->state = SF_ID;
|
||||
sbsf->cmd = SF_ID;
|
||||
break;
|
||||
case CMD_READ_ARRAY_FAST:
|
||||
sbsf->pad_addr_bytes = 1;
|
||||
case CMD_READ_ARRAY_SLOW:
|
||||
case CMD_PAGE_PROGRAM:
|
||||
sbsf->state = SF_ADDR;
|
||||
break;
|
||||
case CMD_WRITE_DISABLE:
|
||||
debug(" write disabled\n");
|
||||
sbsf->status &= ~STAT_WEL;
|
||||
break;
|
||||
case CMD_READ_STATUS:
|
||||
sbsf->state = SF_READ_STATUS;
|
||||
break;
|
||||
case CMD_READ_STATUS1:
|
||||
sbsf->state = SF_READ_STATUS1;
|
||||
break;
|
||||
case CMD_WRITE_ENABLE:
|
||||
debug(" write enabled\n");
|
||||
sbsf->status |= STAT_WEL;
|
||||
break;
|
||||
case CMD_WRITE_STATUS:
|
||||
sbsf->state = SF_WRITE_STATUS;
|
||||
break;
|
||||
default: {
|
||||
int flags = sbsf->data->flags;
|
||||
|
||||
/* we only support erase here */
|
||||
if (sbsf->cmd == CMD_ERASE_CHIP) {
|
||||
sbsf->erase_size = sbsf->data->sector_size *
|
||||
sbsf->data->nr_sectors;
|
||||
} else if (sbsf->cmd == CMD_ERASE_4K && (flags & SECT_4K)) {
|
||||
sbsf->erase_size = 4 << 10;
|
||||
} else if (sbsf->cmd == CMD_ERASE_32K && (flags & SECT_32K)) {
|
||||
sbsf->erase_size = 32 << 10;
|
||||
} else if (sbsf->cmd == CMD_ERASE_64K &&
|
||||
!(flags & (SECT_4K | SECT_32K))) {
|
||||
sbsf->erase_size = 64 << 10;
|
||||
} else {
|
||||
debug(" cmd unknown: %#x\n", sbsf->cmd);
|
||||
return -EIO;
|
||||
}
|
||||
sbsf->state = SF_ADDR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldstate != sbsf->state)
|
||||
debug(" cmd: transition to %s state\n",
|
||||
sandbox_sf_state_name(sbsf->state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_erase_part(struct sandbox_spi_flash *sbsf, int size)
|
||||
{
|
||||
int todo;
|
||||
int ret;
|
||||
|
||||
while (size > 0) {
|
||||
todo = min(size, (int)sizeof(sandbox_sf_0xff));
|
||||
ret = os_write(sbsf->fd, sandbox_sf_0xff, todo);
|
||||
if (ret != todo)
|
||||
return ret;
|
||||
size -= todo;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_sf_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *rxp, void *txp, unsigned long flags)
|
||||
{
|
||||
struct sandbox_spi_flash *sbsf = dev_get_priv(dev);
|
||||
const uint8_t *rx = rxp;
|
||||
uint8_t *tx = txp;
|
||||
uint cnt, pos = 0;
|
||||
int bytes = bitlen / 8;
|
||||
int ret;
|
||||
|
||||
debug("sandbox_sf: state:%x(%s) bytes:%u\n", sbsf->state,
|
||||
sandbox_sf_state_name(sbsf->state), bytes);
|
||||
|
||||
if ((flags & SPI_XFER_BEGIN))
|
||||
sandbox_sf_cs_activate(dev);
|
||||
|
||||
if (sbsf->state == SF_CMD) {
|
||||
/* Figure out the initial state */
|
||||
ret = sandbox_sf_process_cmd(sbsf, rx, tx);
|
||||
if (ret)
|
||||
return ret;
|
||||
++pos;
|
||||
}
|
||||
|
||||
/* Process the remaining data */
|
||||
while (pos < bytes) {
|
||||
switch (sbsf->state) {
|
||||
case SF_ID: {
|
||||
u8 id;
|
||||
|
||||
debug(" id: off:%u tx:", sbsf->off);
|
||||
if (sbsf->off < IDCODE_LEN) {
|
||||
/* Extract correct byte from ID 0x00aabbcc */
|
||||
id = sbsf->data->jedec >>
|
||||
(8 * (IDCODE_LEN - 1 - sbsf->off));
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
debug("%d %02x\n", sbsf->off, id);
|
||||
tx[pos++] = id;
|
||||
++sbsf->off;
|
||||
break;
|
||||
}
|
||||
case SF_ADDR:
|
||||
debug(" addr: bytes:%u rx:%02x ", sbsf->addr_bytes,
|
||||
rx[pos]);
|
||||
|
||||
if (sbsf->addr_bytes++ < SF_ADDR_LEN)
|
||||
sbsf->off = (sbsf->off << 8) | rx[pos];
|
||||
debug("addr:%06x\n", sbsf->off);
|
||||
|
||||
if (tx)
|
||||
sandbox_spi_tristate(&tx[pos], 1);
|
||||
pos++;
|
||||
|
||||
/* See if we're done processing */
|
||||
if (sbsf->addr_bytes <
|
||||
SF_ADDR_LEN + sbsf->pad_addr_bytes)
|
||||
break;
|
||||
|
||||
/* Next state! */
|
||||
if (os_lseek(sbsf->fd, sbsf->off, OS_SEEK_SET) < 0) {
|
||||
puts("sandbox_sf: os_lseek() failed");
|
||||
return -EIO;
|
||||
}
|
||||
switch (sbsf->cmd) {
|
||||
case CMD_READ_ARRAY_FAST:
|
||||
case CMD_READ_ARRAY_SLOW:
|
||||
sbsf->state = SF_READ;
|
||||
break;
|
||||
case CMD_PAGE_PROGRAM:
|
||||
sbsf->state = SF_WRITE;
|
||||
break;
|
||||
default:
|
||||
/* assume erase state ... */
|
||||
sbsf->state = SF_ERASE;
|
||||
goto case_sf_erase;
|
||||
}
|
||||
debug(" cmd: transition to %s state\n",
|
||||
sandbox_sf_state_name(sbsf->state));
|
||||
break;
|
||||
case SF_READ:
|
||||
/*
|
||||
* XXX: need to handle exotic behavior:
|
||||
* - reading past end of device
|
||||
*/
|
||||
|
||||
cnt = bytes - pos;
|
||||
debug(" tx: read(%u)\n", cnt);
|
||||
assert(tx);
|
||||
ret = os_read(sbsf->fd, tx + pos, cnt);
|
||||
if (ret < 0) {
|
||||
puts("sandbox_sf: os_read() failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
pos += ret;
|
||||
break;
|
||||
case SF_READ_STATUS:
|
||||
debug(" read status: %#x\n", sbsf->status);
|
||||
cnt = bytes - pos;
|
||||
memset(tx + pos, sbsf->status, cnt);
|
||||
pos += cnt;
|
||||
break;
|
||||
case SF_READ_STATUS1:
|
||||
debug(" read status: %#x\n", sbsf->status);
|
||||
cnt = bytes - pos;
|
||||
memset(tx + pos, sbsf->status >> 8, cnt);
|
||||
pos += cnt;
|
||||
break;
|
||||
case SF_WRITE_STATUS:
|
||||
debug(" write status: %#x (ignored)\n", rx[pos]);
|
||||
pos = bytes;
|
||||
break;
|
||||
case SF_WRITE:
|
||||
/*
|
||||
* XXX: need to handle exotic behavior:
|
||||
* - unaligned addresses
|
||||
* - more than a page (256) worth of data
|
||||
* - reading past end of device
|
||||
*/
|
||||
if (!(sbsf->status & STAT_WEL)) {
|
||||
puts("sandbox_sf: write enable not set before write\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
cnt = bytes - pos;
|
||||
debug(" rx: write(%u)\n", cnt);
|
||||
if (tx)
|
||||
sandbox_spi_tristate(&tx[pos], cnt);
|
||||
ret = os_write(sbsf->fd, rx + pos, cnt);
|
||||
if (ret < 0) {
|
||||
puts("sandbox_spi: os_write() failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
pos += ret;
|
||||
sbsf->status &= ~STAT_WEL;
|
||||
break;
|
||||
case SF_ERASE:
|
||||
case_sf_erase: {
|
||||
if (!(sbsf->status & STAT_WEL)) {
|
||||
puts("sandbox_sf: write enable not set before erase\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* verify address is aligned */
|
||||
if (sbsf->off & (sbsf->erase_size - 1)) {
|
||||
debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n",
|
||||
sbsf->cmd, sbsf->erase_size,
|
||||
sbsf->off);
|
||||
sbsf->status &= ~STAT_WEL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
debug(" sector erase addr: %u, size: %u\n", sbsf->off,
|
||||
sbsf->erase_size);
|
||||
|
||||
cnt = bytes - pos;
|
||||
if (tx)
|
||||
sandbox_spi_tristate(&tx[pos], cnt);
|
||||
pos += cnt;
|
||||
|
||||
/*
|
||||
* TODO(vapier@gentoo.org): latch WIP in status, and
|
||||
* delay before clearing it ?
|
||||
*/
|
||||
ret = sandbox_erase_part(sbsf, sbsf->erase_size);
|
||||
sbsf->status &= ~STAT_WEL;
|
||||
if (ret) {
|
||||
debug("sandbox_sf: Erase failed\n");
|
||||
goto done;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
default:
|
||||
debug(" ??? no idea what to do ???\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (flags & SPI_XFER_END)
|
||||
sandbox_sf_cs_deactivate(dev);
|
||||
return pos == bytes ? 0 : -EIO;
|
||||
}
|
||||
|
||||
int sandbox_sf_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_spi_flash_plat_data *pdata = dev_get_platdata(dev);
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = dev->of_offset;
|
||||
|
||||
pdata->filename = fdt_getprop(blob, node, "sandbox,filename", NULL);
|
||||
pdata->device_name = fdt_getprop(blob, node, "compatible", NULL);
|
||||
if (!pdata->filename || !pdata->device_name) {
|
||||
debug("%s: Missing properties, filename=%s, device_name=%s\n",
|
||||
__func__, pdata->filename, pdata->device_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_emul_ops sandbox_sf_emul_ops = {
|
||||
.xfer = sandbox_sf_xfer,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SPI_FLASH
|
||||
static int sandbox_cmdline_cb_spi_sf(struct sandbox_state *state,
|
||||
const char *arg)
|
||||
{
|
||||
unsigned long bus, cs;
|
||||
const char *spec = sandbox_spi_parse_spec(arg, &bus, &cs);
|
||||
|
||||
if (!spec)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* It is safe to not make a copy of 'spec' because it comes from the
|
||||
* command line.
|
||||
*
|
||||
* TODO(sjg@chromium.org): It would be nice if we could parse the
|
||||
* spec here, but the problem is that no U-Boot init has been done
|
||||
* yet. Perhaps we can figure something out.
|
||||
*/
|
||||
state->spi[bus][cs].spec = spec;
|
||||
debug("%s: Setting up spec '%s' for bus %ld, cs %ld\n", __func__,
|
||||
spec, bus, cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
SANDBOX_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: <bus>:<cs>:<id>:<file>");
|
||||
|
||||
int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs,
|
||||
struct udevice *bus, int of_offset, const char *spec)
|
||||
{
|
||||
struct udevice *emul;
|
||||
char name[20], *str;
|
||||
struct driver *drv;
|
||||
int ret;
|
||||
|
||||
/* now the emulator */
|
||||
strncpy(name, spec, sizeof(name) - 6);
|
||||
name[sizeof(name) - 6] = '\0';
|
||||
strcat(name, "-emul");
|
||||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
drv = lists_driver_lookup_name("sandbox_sf_emul");
|
||||
if (!drv) {
|
||||
puts("Cannot find sandbox_sf_emul driver\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
ret = device_bind(bus, drv, str, NULL, of_offset, &emul);
|
||||
if (ret) {
|
||||
printf("Cannot create emul device for spec '%s' (err=%d)\n",
|
||||
spec, ret);
|
||||
return ret;
|
||||
}
|
||||
state->spi[busnum][cs].emul = emul;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
dev = state->spi[busnum][cs].emul;
|
||||
device_remove(dev);
|
||||
device_unbind(dev);
|
||||
state->spi[busnum][cs].emul = NULL;
|
||||
}
|
||||
|
||||
static int sandbox_sf_bind_bus_cs(struct sandbox_state *state, int busnum,
|
||||
int cs, const char *spec)
|
||||
{
|
||||
struct udevice *bus, *slave;
|
||||
int ret;
|
||||
|
||||
ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, true, &bus);
|
||||
if (ret) {
|
||||
printf("Invalid bus %d for spec '%s' (err=%d)\n", busnum,
|
||||
spec, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = spi_find_chip_select(bus, cs, &slave);
|
||||
if (!ret) {
|
||||
printf("Chip select %d already exists for spec '%s'\n", cs,
|
||||
spec);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
ret = device_bind_driver(bus, "spi_flash_std", spec, &slave);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sandbox_sf_bind_emul(state, busnum, cs, bus, -1, spec);
|
||||
}
|
||||
|
||||
int sandbox_spi_get_emul(struct sandbox_state *state,
|
||||
struct udevice *bus, struct udevice *slave,
|
||||
struct udevice **emulp)
|
||||
{
|
||||
struct sandbox_spi_info *info;
|
||||
int busnum = bus->seq;
|
||||
int cs = spi_chip_select(slave);
|
||||
int ret;
|
||||
|
||||
info = &state->spi[busnum][cs];
|
||||
if (!info->emul) {
|
||||
/* Use the same device tree node as the SPI flash device */
|
||||
debug("%s: busnum=%u, cs=%u: binding SPI flash emulation: ",
|
||||
__func__, busnum, cs);
|
||||
ret = sandbox_sf_bind_emul(state, busnum, cs, bus,
|
||||
slave->of_offset, slave->name);
|
||||
if (ret) {
|
||||
debug("failed (err=%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
debug("OK\n");
|
||||
}
|
||||
*emulp = info->emul;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_scan_other(bool pre_reloc_only)
|
||||
{
|
||||
struct sandbox_state *state = state_get_current();
|
||||
int busnum, cs;
|
||||
|
||||
if (pre_reloc_only)
|
||||
return 0;
|
||||
for (busnum = 0; busnum < CONFIG_SANDBOX_SPI_MAX_BUS; busnum++) {
|
||||
for (cs = 0; cs < CONFIG_SANDBOX_SPI_MAX_CS; cs++) {
|
||||
const char *spec = state->spi[busnum][cs].spec;
|
||||
int ret;
|
||||
|
||||
if (spec) {
|
||||
ret = sandbox_sf_bind_bus_cs(state, busnum,
|
||||
cs, spec);
|
||||
if (ret) {
|
||||
debug("%s: Bind failed for bus %d, cs %d\n",
|
||||
__func__, busnum, cs);
|
||||
return ret;
|
||||
}
|
||||
debug("%s: Setting up spec '%s' for bus %d, cs %d\n",
|
||||
__func__, spec, busnum, cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct udevice_id sandbox_sf_ids[] = {
|
||||
{ .compatible = "sandbox,spi-flash" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_sf_emul) = {
|
||||
.name = "sandbox_sf_emul",
|
||||
.id = UCLASS_SPI_EMUL,
|
||||
.of_match = sandbox_sf_ids,
|
||||
.ofdata_to_platdata = sandbox_sf_ofdata_to_platdata,
|
||||
.probe = sandbox_sf_probe,
|
||||
.remove = sandbox_sf_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_spi_flash),
|
||||
.platdata_auto_alloc_size = sizeof(struct sandbox_spi_flash_plat_data),
|
||||
.ops = &sandbox_sf_emul_ops,
|
||||
};
|
||||
103
u-boot/drivers/mtd/spi/sf-uclass.c
Normal file
103
u-boot/drivers/mtd/spi/sf-uclass.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include "sf_internal.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int spi_flash_read_dm(struct udevice *dev, u32 offset, size_t len, void *buf)
|
||||
{
|
||||
return sf_get_ops(dev)->read(dev, offset, len, buf);
|
||||
}
|
||||
|
||||
int spi_flash_write_dm(struct udevice *dev, u32 offset, size_t len,
|
||||
const void *buf)
|
||||
{
|
||||
return sf_get_ops(dev)->write(dev, offset, len, buf);
|
||||
}
|
||||
|
||||
int spi_flash_erase_dm(struct udevice *dev, u32 offset, size_t len)
|
||||
{
|
||||
return sf_get_ops(dev)->erase(dev, offset, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO(sjg@chromium.org): This is an old-style function. We should remove
|
||||
* it when all SPI flash drivers use dm
|
||||
*/
|
||||
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int spi_mode)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
if (spi_flash_probe_bus_cs(bus, cs, max_hz, spi_mode, &dev))
|
||||
return NULL;
|
||||
|
||||
return dev_get_uclass_priv(dev);
|
||||
}
|
||||
|
||||
void spi_flash_free(struct spi_flash *flash)
|
||||
{
|
||||
device_remove(flash->spi->dev);
|
||||
}
|
||||
|
||||
int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int spi_mode,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct spi_slave *slave;
|
||||
struct udevice *bus;
|
||||
char *str;
|
||||
int ret;
|
||||
|
||||
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_USE_TINY_PRINTF)
|
||||
str = "spi_flash";
|
||||
#else
|
||||
char name[30];
|
||||
|
||||
snprintf(name, sizeof(name), "spi_flash@%d:%d", busnum, cs);
|
||||
str = strdup(name);
|
||||
#endif
|
||||
ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode,
|
||||
"spi_flash_std", str, &bus, &slave);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*devp = slave->dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_flash_post_bind(struct udevice *dev)
|
||||
{
|
||||
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
|
||||
struct dm_spi_flash_ops *ops = sf_get_ops(dev);
|
||||
static int reloc_done;
|
||||
|
||||
if (!reloc_done) {
|
||||
if (ops->read)
|
||||
ops->read += gd->reloc_off;
|
||||
if (ops->write)
|
||||
ops->write += gd->reloc_off;
|
||||
if (ops->erase)
|
||||
ops->erase += gd->reloc_off;
|
||||
|
||||
reloc_done++;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(spi_flash) = {
|
||||
.id = UCLASS_SPI_FLASH,
|
||||
.name = "spi_flash",
|
||||
.post_bind = spi_flash_post_bind,
|
||||
.per_device_auto_alloc_size = sizeof(struct spi_flash),
|
||||
};
|
||||
58
u-boot/drivers/mtd/spi/sf.c
Normal file
58
u-boot/drivers/mtd/spi/sf.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPI flash interface
|
||||
*
|
||||
* Copyright (C) 2008 Atmel Corporation
|
||||
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <spi.h>
|
||||
|
||||
static int spi_flash_read_write(struct spi_slave *spi,
|
||||
const u8 *cmd, size_t cmd_len,
|
||||
const u8 *data_out, u8 *data_in,
|
||||
size_t data_len)
|
||||
{
|
||||
unsigned long flags = SPI_XFER_BEGIN;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_SF_DUAL_FLASH
|
||||
if (spi->flags & SPI_XFER_U_PAGE)
|
||||
flags |= SPI_XFER_U_PAGE;
|
||||
#endif
|
||||
if (data_len == 0)
|
||||
flags |= SPI_XFER_END;
|
||||
|
||||
ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
|
||||
if (ret) {
|
||||
debug("SF: Failed to send command (%zu bytes): %d\n",
|
||||
cmd_len, ret);
|
||||
} else if (data_len != 0) {
|
||||
ret = spi_xfer(spi, data_len * 8, data_out, data_in,
|
||||
SPI_XFER_END);
|
||||
if (ret)
|
||||
debug("SF: Failed to transfer %zu bytes of data: %d\n",
|
||||
data_len, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
|
||||
size_t cmd_len, void *data, size_t data_len)
|
||||
{
|
||||
return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len);
|
||||
}
|
||||
|
||||
int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
|
||||
{
|
||||
return spi_flash_cmd_read(spi, &cmd, 1, response, len);
|
||||
}
|
||||
|
||||
int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
|
||||
const void *data, size_t data_len)
|
||||
{
|
||||
return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);
|
||||
}
|
||||
701
u-boot/drivers/mtd/spi/sf_dataflash.c
Normal file
701
u-boot/drivers/mtd/spi/sf_dataflash.c
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
*
|
||||
* Atmel DataFlash probing
|
||||
*
|
||||
* Copyright (C) 2004-2009, 2015 Freescale Semiconductor, Inc.
|
||||
* Haikun Wang (haikun.wang@freescale.com)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
#include <div64.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/math64.h>
|
||||
|
||||
#include "sf_internal.h"
|
||||
|
||||
/* reads can bypass the buffers */
|
||||
#define OP_READ_CONTINUOUS 0xE8
|
||||
#define OP_READ_PAGE 0xD2
|
||||
|
||||
/* group B requests can run even while status reports "busy" */
|
||||
#define OP_READ_STATUS 0xD7 /* group B */
|
||||
|
||||
/* move data between host and buffer */
|
||||
#define OP_READ_BUFFER1 0xD4 /* group B */
|
||||
#define OP_READ_BUFFER2 0xD6 /* group B */
|
||||
#define OP_WRITE_BUFFER1 0x84 /* group B */
|
||||
#define OP_WRITE_BUFFER2 0x87 /* group B */
|
||||
|
||||
/* erasing flash */
|
||||
#define OP_ERASE_PAGE 0x81
|
||||
#define OP_ERASE_BLOCK 0x50
|
||||
|
||||
/* move data between buffer and flash */
|
||||
#define OP_TRANSFER_BUF1 0x53
|
||||
#define OP_TRANSFER_BUF2 0x55
|
||||
#define OP_MREAD_BUFFER1 0xD4
|
||||
#define OP_MREAD_BUFFER2 0xD6
|
||||
#define OP_MWERASE_BUFFER1 0x83
|
||||
#define OP_MWERASE_BUFFER2 0x86
|
||||
#define OP_MWRITE_BUFFER1 0x88 /* sector must be pre-erased */
|
||||
#define OP_MWRITE_BUFFER2 0x89 /* sector must be pre-erased */
|
||||
|
||||
/* write to buffer, then write-erase to flash */
|
||||
#define OP_PROGRAM_VIA_BUF1 0x82
|
||||
#define OP_PROGRAM_VIA_BUF2 0x85
|
||||
|
||||
/* compare buffer to flash */
|
||||
#define OP_COMPARE_BUF1 0x60
|
||||
#define OP_COMPARE_BUF2 0x61
|
||||
|
||||
/* read flash to buffer, then write-erase to flash */
|
||||
#define OP_REWRITE_VIA_BUF1 0x58
|
||||
#define OP_REWRITE_VIA_BUF2 0x59
|
||||
|
||||
/*
|
||||
* newer chips report JEDEC manufacturer and device IDs; chip
|
||||
* serial number and OTP bits; and per-sector writeprotect.
|
||||
*/
|
||||
#define OP_READ_ID 0x9F
|
||||
#define OP_READ_SECURITY 0x77
|
||||
#define OP_WRITE_SECURITY_REVC 0x9A
|
||||
#define OP_WRITE_SECURITY 0x9B /* revision D */
|
||||
|
||||
|
||||
struct dataflash {
|
||||
uint8_t command[16];
|
||||
unsigned short page_offset; /* offset in flash address */
|
||||
};
|
||||
|
||||
/*
|
||||
* Return the status of the DataFlash device.
|
||||
*/
|
||||
static inline int dataflash_status(struct spi_slave *spi)
|
||||
{
|
||||
int ret;
|
||||
u8 status;
|
||||
/*
|
||||
* NOTE: at45db321c over 25 MHz wants to write
|
||||
* a dummy byte after the opcode...
|
||||
*/
|
||||
ret = spi_flash_cmd(spi, OP_READ_STATUS, &status, 1);
|
||||
return ret ? -EIO : status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the DataFlash device until it is READY.
|
||||
* This usually takes 5-20 msec or so; more for sector erase.
|
||||
* ready: return > 0
|
||||
*/
|
||||
static int dataflash_waitready(struct spi_slave *spi)
|
||||
{
|
||||
int status;
|
||||
int timeout = 2 * CONFIG_SYS_HZ;
|
||||
int timebase;
|
||||
|
||||
timebase = get_timer(0);
|
||||
do {
|
||||
status = dataflash_status(spi);
|
||||
if (status < 0)
|
||||
status = 0;
|
||||
|
||||
if (status & (1 << 7)) /* RDY/nBSY */
|
||||
return status;
|
||||
|
||||
mdelay(3);
|
||||
} while (get_timer(timebase) < timeout);
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase pages of flash.
|
||||
*/
|
||||
static int spi_dataflash_erase(struct udevice *dev, u32 offset, size_t len)
|
||||
{
|
||||
struct dataflash *dataflash;
|
||||
struct spi_flash *spi_flash;
|
||||
struct spi_slave *spi;
|
||||
unsigned blocksize;
|
||||
uint8_t *command;
|
||||
uint32_t rem;
|
||||
int status;
|
||||
|
||||
dataflash = dev_get_priv(dev);
|
||||
spi_flash = dev_get_uclass_priv(dev);
|
||||
spi = spi_flash->spi;
|
||||
|
||||
blocksize = spi_flash->page_size << 3;
|
||||
|
||||
memset(dataflash->command, 0 , sizeof(dataflash->command));
|
||||
command = dataflash->command;
|
||||
|
||||
debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len);
|
||||
|
||||
div_u64_rem(len, spi_flash->page_size, &rem);
|
||||
if (rem)
|
||||
return -EINVAL;
|
||||
div_u64_rem(offset, spi_flash->page_size, &rem);
|
||||
if (rem)
|
||||
return -EINVAL;
|
||||
|
||||
status = spi_claim_bus(spi);
|
||||
if (status) {
|
||||
debug("SPI DATAFLASH: unable to claim SPI bus\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
unsigned int pageaddr;
|
||||
int do_block;
|
||||
/*
|
||||
* Calculate flash page address; use block erase (for speed) if
|
||||
* we're at a block boundary and need to erase the whole block.
|
||||
*/
|
||||
pageaddr = div_u64(offset, spi_flash->page_size);
|
||||
do_block = (pageaddr & 0x7) == 0 && len >= blocksize;
|
||||
pageaddr = pageaddr << dataflash->page_offset;
|
||||
|
||||
command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
|
||||
command[1] = (uint8_t)(pageaddr >> 16);
|
||||
command[2] = (uint8_t)(pageaddr >> 8);
|
||||
command[3] = 0;
|
||||
|
||||
debug("%s ERASE %s: (%x) %x %x %x [%d]\n",
|
||||
dev->name, do_block ? "block" : "page",
|
||||
command[0], command[1], command[2], command[3],
|
||||
pageaddr);
|
||||
|
||||
status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
|
||||
if (status < 0) {
|
||||
debug("%s: erase send command error!\n", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = dataflash_waitready(spi);
|
||||
if (status < 0) {
|
||||
debug("%s: erase waitready error!\n", dev->name);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (do_block) {
|
||||
offset += blocksize;
|
||||
len -= blocksize;
|
||||
} else {
|
||||
offset += spi_flash->page_size;
|
||||
len -= spi_flash->page_size;
|
||||
}
|
||||
}
|
||||
|
||||
spi_release_bus(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from the DataFlash device.
|
||||
* offset : Start offset in flash device
|
||||
* len : Amount to read
|
||||
* buf : Buffer containing the data
|
||||
*/
|
||||
static int spi_dataflash_read(struct udevice *dev, u32 offset, size_t len,
|
||||
void *buf)
|
||||
{
|
||||
struct dataflash *dataflash;
|
||||
struct spi_flash *spi_flash;
|
||||
struct spi_slave *spi;
|
||||
unsigned int addr;
|
||||
uint8_t *command;
|
||||
int status;
|
||||
|
||||
dataflash = dev_get_priv(dev);
|
||||
spi_flash = dev_get_uclass_priv(dev);
|
||||
spi = spi_flash->spi;
|
||||
|
||||
memset(dataflash->command, 0 , sizeof(dataflash->command));
|
||||
command = dataflash->command;
|
||||
|
||||
debug("%s: erase addr=0x%x len 0x%x\n", dev->name, offset, len);
|
||||
debug("READ: (%x) %x %x %x\n",
|
||||
command[0], command[1], command[2], command[3]);
|
||||
|
||||
/* Calculate flash page/byte address */
|
||||
addr = (((unsigned)offset / spi_flash->page_size)
|
||||
<< dataflash->page_offset)
|
||||
+ ((unsigned)offset % spi_flash->page_size);
|
||||
|
||||
status = spi_claim_bus(spi);
|
||||
if (status) {
|
||||
debug("SPI DATAFLASH: unable to claim SPI bus\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Continuous read, max clock = f(car) which may be less than
|
||||
* the peak rate available. Some chips support commands with
|
||||
* fewer "don't care" bytes. Both buffers stay unchanged.
|
||||
*/
|
||||
command[0] = OP_READ_CONTINUOUS;
|
||||
command[1] = (uint8_t)(addr >> 16);
|
||||
command[2] = (uint8_t)(addr >> 8);
|
||||
command[3] = (uint8_t)(addr >> 0);
|
||||
|
||||
/* plus 4 "don't care" bytes, command len: 4 + 4 "don't care" bytes */
|
||||
status = spi_flash_cmd_read(spi, command, 8, buf, len);
|
||||
|
||||
spi_release_bus(spi);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to the DataFlash device.
|
||||
* offset : Start offset in flash device
|
||||
* len : Amount to write
|
||||
* buf : Buffer containing the data
|
||||
*/
|
||||
int spi_dataflash_write(struct udevice *dev, u32 offset, size_t len,
|
||||
const void *buf)
|
||||
{
|
||||
struct dataflash *dataflash;
|
||||
struct spi_flash *spi_flash;
|
||||
struct spi_slave *spi;
|
||||
uint8_t *command;
|
||||
unsigned int pageaddr, addr, to, writelen;
|
||||
size_t remaining = len;
|
||||
u_char *writebuf = (u_char *)buf;
|
||||
int status = -EINVAL;
|
||||
|
||||
dataflash = dev_get_priv(dev);
|
||||
spi_flash = dev_get_uclass_priv(dev);
|
||||
spi = spi_flash->spi;
|
||||
|
||||
memset(dataflash->command, 0 , sizeof(dataflash->command));
|
||||
command = dataflash->command;
|
||||
|
||||
debug("%s: write 0x%x..0x%x\n", dev->name, offset, (offset + len));
|
||||
|
||||
pageaddr = ((unsigned)offset / spi_flash->page_size);
|
||||
to = ((unsigned)offset % spi_flash->page_size);
|
||||
if (to + len > spi_flash->page_size)
|
||||
writelen = spi_flash->page_size - to;
|
||||
else
|
||||
writelen = len;
|
||||
|
||||
status = spi_claim_bus(spi);
|
||||
if (status) {
|
||||
debug("SPI DATAFLASH: unable to claim SPI bus\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
while (remaining > 0) {
|
||||
debug("write @ %d:%d len=%d\n", pageaddr, to, writelen);
|
||||
|
||||
/*
|
||||
* REVISIT:
|
||||
* (a) each page in a sector must be rewritten at least
|
||||
* once every 10K sibling erase/program operations.
|
||||
* (b) for pages that are already erased, we could
|
||||
* use WRITE+MWRITE not PROGRAM for ~30% speedup.
|
||||
* (c) WRITE to buffer could be done while waiting for
|
||||
* a previous MWRITE/MWERASE to complete ...
|
||||
* (d) error handling here seems to be mostly missing.
|
||||
*
|
||||
* Two persistent bits per page, plus a per-sector counter,
|
||||
* could support (a) and (b) ... we might consider using
|
||||
* the second half of sector zero, which is just one block,
|
||||
* to track that state. (On AT91, that sector should also
|
||||
* support boot-from-DataFlash.)
|
||||
*/
|
||||
|
||||
addr = pageaddr << dataflash->page_offset;
|
||||
|
||||
/* (1) Maybe transfer partial page to Buffer1 */
|
||||
if (writelen != spi_flash->page_size) {
|
||||
command[0] = OP_TRANSFER_BUF1;
|
||||
command[1] = (addr & 0x00FF0000) >> 16;
|
||||
command[2] = (addr & 0x0000FF00) >> 8;
|
||||
command[3] = 0;
|
||||
|
||||
debug("TRANSFER: (%x) %x %x %x\n",
|
||||
command[0], command[1], command[2], command[3]);
|
||||
|
||||
status = spi_flash_cmd_write(spi, command, 4, NULL, 0);
|
||||
if (status < 0) {
|
||||
debug("%s: write(<pagesize) command error!\n",
|
||||
dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = dataflash_waitready(spi);
|
||||
if (status < 0) {
|
||||
debug("%s: write(<pagesize) waitready error!\n",
|
||||
dev->name);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* (2) Program full page via Buffer1 */
|
||||
addr += to;
|
||||
command[0] = OP_PROGRAM_VIA_BUF1;
|
||||
command[1] = (addr & 0x00FF0000) >> 16;
|
||||
command[2] = (addr & 0x0000FF00) >> 8;
|
||||
command[3] = (addr & 0x000000FF);
|
||||
|
||||
debug("PROGRAM: (%x) %x %x %x\n",
|
||||
command[0], command[1], command[2], command[3]);
|
||||
|
||||
status = spi_flash_cmd_write(spi, command,
|
||||
4, writebuf, writelen);
|
||||
if (status < 0) {
|
||||
debug("%s: write send command error!\n", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = dataflash_waitready(spi);
|
||||
if (status < 0) {
|
||||
debug("%s: write waitready error!\n", dev->name);
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_DATAFLASH_WRITE_VERIFY
|
||||
/* (3) Compare to Buffer1 */
|
||||
addr = pageaddr << dataflash->page_offset;
|
||||
command[0] = OP_COMPARE_BUF1;
|
||||
command[1] = (addr & 0x00FF0000) >> 16;
|
||||
command[2] = (addr & 0x0000FF00) >> 8;
|
||||
command[3] = 0;
|
||||
|
||||
debug("COMPARE: (%x) %x %x %x\n",
|
||||
command[0], command[1], command[2], command[3]);
|
||||
|
||||
status = spi_flash_cmd_write(spi, command,
|
||||
4, writebuf, writelen);
|
||||
if (status < 0) {
|
||||
debug("%s: write(compare) send command error!\n",
|
||||
dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = dataflash_waitready(spi);
|
||||
|
||||
/* Check result of the compare operation */
|
||||
if (status & (1 << 6)) {
|
||||
printf("SPI DataFlash: write compare page %u, err %d\n",
|
||||
pageaddr, status);
|
||||
remaining = 0;
|
||||
status = -EIO;
|
||||
break;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SPI_DATAFLASH_WRITE_VERIFY */
|
||||
remaining = remaining - writelen;
|
||||
pageaddr++;
|
||||
to = 0;
|
||||
writebuf += writelen;
|
||||
|
||||
if (remaining > spi_flash->page_size)
|
||||
writelen = spi_flash->page_size;
|
||||
else
|
||||
writelen = remaining;
|
||||
}
|
||||
|
||||
spi_release_bus(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_dataflash(struct udevice *dev, char *name, int nr_pages,
|
||||
int pagesize, int pageoffset, char revision)
|
||||
{
|
||||
struct spi_flash *spi_flash;
|
||||
struct dataflash *dataflash;
|
||||
|
||||
dataflash = dev_get_priv(dev);
|
||||
spi_flash = dev_get_uclass_priv(dev);
|
||||
|
||||
dataflash->page_offset = pageoffset;
|
||||
|
||||
spi_flash->name = name;
|
||||
spi_flash->page_size = pagesize;
|
||||
spi_flash->size = nr_pages * pagesize;
|
||||
spi_flash->erase_size = pagesize;
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
printf("SPI DataFlash: Detected %s with page size ", spi_flash->name);
|
||||
print_size(spi_flash->page_size, ", erase size ");
|
||||
print_size(spi_flash->erase_size, ", total ");
|
||||
print_size(spi_flash->size, "");
|
||||
printf(", revision %c", revision);
|
||||
puts("\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct flash_info {
|
||||
char *name;
|
||||
|
||||
/*
|
||||
* JEDEC id has a high byte of zero plus three data bytes:
|
||||
* the manufacturer id, then a two byte device id.
|
||||
*/
|
||||
uint32_t jedec_id;
|
||||
|
||||
/* The size listed here is what works with OP_ERASE_PAGE. */
|
||||
unsigned nr_pages;
|
||||
uint16_t pagesize;
|
||||
uint16_t pageoffset;
|
||||
|
||||
uint16_t flags;
|
||||
#define SUP_POW2PS 0x0002 /* supports 2^N byte pages */
|
||||
#define IS_POW2PS 0x0001 /* uses 2^N byte pages */
|
||||
};
|
||||
|
||||
static struct flash_info dataflash_data[] = {
|
||||
/*
|
||||
* NOTE: chips with SUP_POW2PS (rev D and up) need two entries,
|
||||
* one with IS_POW2PS and the other without. The entry with the
|
||||
* non-2^N byte page size can't name exact chip revisions without
|
||||
* losing backwards compatibility for cmdlinepart.
|
||||
*
|
||||
* Those two entries have different name spelling format in order to
|
||||
* show their difference obviously.
|
||||
* The upper case refer to the chip isn't in normal 2^N bytes page-size
|
||||
* mode.
|
||||
* The lower case refer to the chip is in normal 2^N bytes page-size
|
||||
* mode.
|
||||
*
|
||||
* These newer chips also support 128-byte security registers (with
|
||||
* 64 bytes one-time-programmable) and software write-protection.
|
||||
*/
|
||||
{ "AT45DB011B", 0x1f2200, 512, 264, 9, SUP_POW2PS},
|
||||
{ "at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS},
|
||||
|
||||
{ "AT45DB021B", 0x1f2300, 1024, 264, 9, SUP_POW2PS},
|
||||
{ "at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS},
|
||||
|
||||
{ "AT45DB041x", 0x1f2400, 2048, 264, 9, SUP_POW2PS},
|
||||
{ "at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS},
|
||||
|
||||
{ "AT45DB081B", 0x1f2500, 4096, 264, 9, SUP_POW2PS},
|
||||
{ "at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS},
|
||||
|
||||
{ "AT45DB161x", 0x1f2600, 4096, 528, 10, SUP_POW2PS},
|
||||
{ "at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS},
|
||||
|
||||
{ "AT45DB321x", 0x1f2700, 8192, 528, 10, 0}, /* rev C */
|
||||
|
||||
{ "AT45DB321x", 0x1f2701, 8192, 528, 10, SUP_POW2PS},
|
||||
{ "at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS},
|
||||
|
||||
{ "AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS},
|
||||
{ "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
|
||||
};
|
||||
|
||||
static struct flash_info *jedec_probe(struct spi_slave *spi, u8 *id)
|
||||
{
|
||||
int tmp;
|
||||
uint32_t jedec;
|
||||
struct flash_info *info;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* JEDEC also defines an optional "extended device information"
|
||||
* string for after vendor-specific data, after the three bytes
|
||||
* we use here. Supporting some chips might require using it.
|
||||
*
|
||||
* If the vendor ID isn't Atmel's (0x1f), assume this call failed.
|
||||
* That's not an error; only rev C and newer chips handle it, and
|
||||
* only Atmel sells these chips.
|
||||
*/
|
||||
if (id[0] != 0x1f)
|
||||
return NULL;
|
||||
|
||||
jedec = id[0];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[1];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[2];
|
||||
|
||||
for (tmp = 0, info = dataflash_data;
|
||||
tmp < ARRAY_SIZE(dataflash_data);
|
||||
tmp++, info++) {
|
||||
if (info->jedec_id == jedec) {
|
||||
if (info->flags & SUP_POW2PS) {
|
||||
status = dataflash_status(spi);
|
||||
if (status < 0) {
|
||||
debug("SPI DataFlash: status error %d\n",
|
||||
status);
|
||||
return NULL;
|
||||
}
|
||||
if (status & 0x1) {
|
||||
if (info->flags & IS_POW2PS)
|
||||
return info;
|
||||
} else {
|
||||
if (!(info->flags & IS_POW2PS))
|
||||
return info;
|
||||
}
|
||||
} else {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Treat other chips as errors ... we won't know the right page
|
||||
* size (it might be binary) even when we can tell which density
|
||||
* class is involved (legacy chip id scheme).
|
||||
*/
|
||||
printf("SPI DataFlash: Unsupported flash IDs: ");
|
||||
printf("manuf %02x, jedec %04x, ext_jedec %04x\n",
|
||||
id[0], jedec, id[3] << 8 | id[4]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect and initialize DataFlash device, using JEDEC IDs on newer chips
|
||||
* or else the ID code embedded in the status bits:
|
||||
*
|
||||
* Device Density ID code #Pages PageSize Offset
|
||||
* AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9
|
||||
* AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9
|
||||
* AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9
|
||||
* AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9
|
||||
* AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10
|
||||
* AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10
|
||||
* AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11
|
||||
* AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11
|
||||
*/
|
||||
static int spi_dataflash_probe(struct udevice *dev)
|
||||
{
|
||||
struct spi_slave *spi = dev_get_parent_priv(dev);
|
||||
struct spi_flash *spi_flash;
|
||||
struct flash_info *info;
|
||||
u8 idcode[5];
|
||||
int ret, status = 0;
|
||||
|
||||
spi_flash = dev_get_uclass_priv(dev);
|
||||
spi_flash->dev = dev;
|
||||
|
||||
ret = spi_claim_bus(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode));
|
||||
if (ret) {
|
||||
printf("SPI DataFlash: Failed to get idcodes\n");
|
||||
goto err_read_cmd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to detect dataflash by JEDEC ID.
|
||||
* If it succeeds we know we have either a C or D part.
|
||||
* D will support power of 2 pagesize option.
|
||||
* Both support the security register, though with different
|
||||
* write procedures.
|
||||
*/
|
||||
info = jedec_probe(spi, idcode);
|
||||
if (info != NULL)
|
||||
add_dataflash(dev, info->name, info->nr_pages,
|
||||
info->pagesize, info->pageoffset,
|
||||
(info->flags & SUP_POW2PS) ? 'd' : 'c');
|
||||
else {
|
||||
/*
|
||||
* Older chips support only legacy commands, identifing
|
||||
* capacity using bits in the status byte.
|
||||
*/
|
||||
status = dataflash_status(spi);
|
||||
if (status <= 0 || status == 0xff) {
|
||||
printf("SPI DataFlash: read status error %d\n", status);
|
||||
if (status == 0 || status == 0xff)
|
||||
status = -ENODEV;
|
||||
goto err_read_cmd;
|
||||
}
|
||||
/*
|
||||
* if there's a device there, assume it's dataflash.
|
||||
* board setup should have set spi->max_speed_max to
|
||||
* match f(car) for continuous reads, mode 0 or 3.
|
||||
*/
|
||||
switch (status & 0x3c) {
|
||||
case 0x0c: /* 0 0 1 1 x x */
|
||||
status = add_dataflash(dev, "AT45DB011B",
|
||||
512, 264, 9, 0);
|
||||
break;
|
||||
case 0x14: /* 0 1 0 1 x x */
|
||||
status = add_dataflash(dev, "AT45DB021B",
|
||||
1024, 264, 9, 0);
|
||||
break;
|
||||
case 0x1c: /* 0 1 1 1 x x */
|
||||
status = add_dataflash(dev, "AT45DB041x",
|
||||
2048, 264, 9, 0);
|
||||
break;
|
||||
case 0x24: /* 1 0 0 1 x x */
|
||||
status = add_dataflash(dev, "AT45DB081B",
|
||||
4096, 264, 9, 0);
|
||||
break;
|
||||
case 0x2c: /* 1 0 1 1 x x */
|
||||
status = add_dataflash(dev, "AT45DB161x",
|
||||
4096, 528, 10, 0);
|
||||
break;
|
||||
case 0x34: /* 1 1 0 1 x x */
|
||||
status = add_dataflash(dev, "AT45DB321x",
|
||||
8192, 528, 10, 0);
|
||||
break;
|
||||
case 0x38: /* 1 1 1 x x x */
|
||||
case 0x3c:
|
||||
status = add_dataflash(dev, "AT45DB642x",
|
||||
8192, 1056, 11, 0);
|
||||
break;
|
||||
/* obsolete AT45DB1282 not (yet?) supported */
|
||||
default:
|
||||
dev_info(&spi->dev, "unsupported device (%x)\n",
|
||||
status & 0x3c);
|
||||
status = -ENODEV;
|
||||
goto err_read_cmd;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign spi data */
|
||||
spi_flash->spi = spi;
|
||||
spi_flash->memory_map = spi->memory_map;
|
||||
spi_flash->dual_flash = spi->option;
|
||||
|
||||
spi_release_bus(spi);
|
||||
|
||||
return 0;
|
||||
|
||||
err_read_cmd:
|
||||
spi_release_bus(spi);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct dm_spi_flash_ops spi_dataflash_ops = {
|
||||
.read = spi_dataflash_read,
|
||||
.write = spi_dataflash_write,
|
||||
.erase = spi_dataflash_erase,
|
||||
};
|
||||
|
||||
static const struct udevice_id spi_dataflash_ids[] = {
|
||||
{ .compatible = "atmel,at45", },
|
||||
{ .compatible = "atmel,dataflash", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(spi_dataflash) = {
|
||||
.name = "spi_dataflash",
|
||||
.id = UCLASS_SPI_FLASH,
|
||||
.of_match = spi_dataflash_ids,
|
||||
.probe = spi_dataflash_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct dataflash),
|
||||
.ops = &spi_dataflash_ops,
|
||||
};
|
||||
248
u-boot/drivers/mtd/spi/sf_internal.h
Normal file
248
u-boot/drivers/mtd/spi/sf_internal.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* SPI flash internal definitions
|
||||
*
|
||||
* Copyright (C) 2008 Atmel Corporation
|
||||
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef _SF_INTERNAL_H_
|
||||
#define _SF_INTERNAL_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/* Dual SPI flash memories - see SPI_COMM_DUAL_... */
|
||||
enum spi_dual_flash {
|
||||
SF_SINGLE_FLASH = 0,
|
||||
SF_DUAL_STACKED_FLASH = BIT(0),
|
||||
SF_DUAL_PARALLEL_FLASH = BIT(1),
|
||||
};
|
||||
|
||||
/* Enum list - Full read commands */
|
||||
enum spi_read_cmds {
|
||||
ARRAY_SLOW = BIT(0),
|
||||
ARRAY_FAST = BIT(1),
|
||||
DUAL_OUTPUT_FAST = BIT(2),
|
||||
QUAD_OUTPUT_FAST = BIT(3),
|
||||
DUAL_IO_FAST = BIT(4),
|
||||
QUAD_IO_FAST = BIT(5),
|
||||
};
|
||||
|
||||
/* Normal - Extended - Full command set */
|
||||
#define RD_NORM (ARRAY_SLOW | ARRAY_FAST)
|
||||
#define RD_EXTN (RD_NORM | DUAL_OUTPUT_FAST | DUAL_IO_FAST)
|
||||
#define RD_FULL (RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST)
|
||||
|
||||
/* sf param flags */
|
||||
enum {
|
||||
#ifndef CONFIG_SPI_FLASH_USE_4K_SECTORS
|
||||
SECT_4K = 0,
|
||||
#else
|
||||
SECT_4K = BIT(0),
|
||||
#endif
|
||||
SECT_32K = BIT(1),
|
||||
E_FSR = BIT(2),
|
||||
SST_WR = BIT(3),
|
||||
WR_QPP = BIT(4),
|
||||
};
|
||||
|
||||
enum spi_nor_option_flags {
|
||||
SNOR_F_SST_WR = BIT(0),
|
||||
SNOR_F_USE_FSR = BIT(1),
|
||||
};
|
||||
|
||||
#define SPI_FLASH_3B_ADDR_LEN 3
|
||||
#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN)
|
||||
#define SPI_FLASH_16MB_BOUN 0x1000000
|
||||
|
||||
/* CFI Manufacture ID's */
|
||||
#define SPI_FLASH_CFI_MFR_SPANSION 0x01
|
||||
#define SPI_FLASH_CFI_MFR_STMICRO 0x20
|
||||
#define SPI_FLASH_CFI_MFR_MACRONIX 0xc2
|
||||
#define SPI_FLASH_CFI_MFR_SST 0xbf
|
||||
#define SPI_FLASH_CFI_MFR_WINBOND 0xef
|
||||
#define SPI_FLASH_CFI_MFR_ATMEL 0x1f
|
||||
|
||||
/* Erase commands */
|
||||
#define CMD_ERASE_4K 0x20
|
||||
#define CMD_ERASE_32K 0x52
|
||||
#define CMD_ERASE_CHIP 0xc7
|
||||
#define CMD_ERASE_64K 0xd8
|
||||
|
||||
/* Write commands */
|
||||
#define CMD_WRITE_STATUS 0x01
|
||||
#define CMD_PAGE_PROGRAM 0x02
|
||||
#define CMD_WRITE_DISABLE 0x04
|
||||
#define CMD_WRITE_ENABLE 0x06
|
||||
#define CMD_QUAD_PAGE_PROGRAM 0x32
|
||||
#define CMD_WRITE_EVCR 0x61
|
||||
|
||||
/* Read commands */
|
||||
#define CMD_READ_ARRAY_SLOW 0x03
|
||||
#define CMD_READ_ARRAY_FAST 0x0b
|
||||
#define CMD_READ_DUAL_OUTPUT_FAST 0x3b
|
||||
#define CMD_READ_DUAL_IO_FAST 0xbb
|
||||
#define CMD_READ_QUAD_OUTPUT_FAST 0x6b
|
||||
#define CMD_READ_QUAD_IO_FAST 0xeb
|
||||
#define CMD_READ_ID 0x9f
|
||||
#define CMD_READ_STATUS 0x05
|
||||
#define CMD_READ_STATUS1 0x35
|
||||
#define CMD_READ_CONFIG 0x35
|
||||
#define CMD_FLAG_STATUS 0x70
|
||||
#define CMD_READ_EVCR 0x65
|
||||
|
||||
/* Bank addr access commands */
|
||||
#ifdef CONFIG_SPI_FLASH_BAR
|
||||
# define CMD_BANKADDR_BRWR 0x17
|
||||
# define CMD_BANKADDR_BRRD 0x16
|
||||
# define CMD_EXTNADDR_WREAR 0xC5
|
||||
# define CMD_EXTNADDR_RDEAR 0xC8
|
||||
#endif
|
||||
|
||||
/* Common status */
|
||||
#define STATUS_WIP BIT(0)
|
||||
#define STATUS_QEB_WINSPAN BIT(1)
|
||||
#define STATUS_QEB_MXIC BIT(6)
|
||||
#define STATUS_PEC BIT(7)
|
||||
#define STATUS_QEB_MICRON BIT(7)
|
||||
#define SR_BP0 BIT(2) /* Block protect 0 */
|
||||
#define SR_BP1 BIT(3) /* Block protect 1 */
|
||||
#define SR_BP2 BIT(4) /* Block protect 2 */
|
||||
|
||||
/* Flash timeout values */
|
||||
#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ)
|
||||
#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ)
|
||||
#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ)
|
||||
|
||||
/* SST specific */
|
||||
#ifdef CONFIG_SPI_FLASH_SST
|
||||
# define CMD_SST_BP 0x02 /* Byte Program */
|
||||
# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */
|
||||
|
||||
int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
|
||||
const void *buf);
|
||||
int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len,
|
||||
const void *buf);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPI_FLASH_SPANSION
|
||||
/* Used for Spansion S25FS-S family flash only. */
|
||||
#define CMD_SPANSION_RDAR 0x65 /* Read any device register */
|
||||
#define CMD_SPANSION_WRAR 0x71 /* Write any device register */
|
||||
#endif
|
||||
/**
|
||||
* struct spi_flash_params - SPI/QSPI flash device params structure
|
||||
*
|
||||
* @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO])
|
||||
* @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id])
|
||||
* @ext_jedec: Device ext_jedec ID
|
||||
* @sector_size: Isn't necessarily a sector size from vendor,
|
||||
* the size listed here is what works with CMD_ERASE_64K
|
||||
* @nr_sectors: No.of sectors on this device
|
||||
* @e_rd_cmd: Enum list for read commands
|
||||
* @flags: Important param, for flash specific behaviour
|
||||
*/
|
||||
struct spi_flash_params {
|
||||
const char *name;
|
||||
u32 jedec;
|
||||
u16 ext_jedec;
|
||||
u32 sector_size;
|
||||
u32 nr_sectors;
|
||||
u8 e_rd_cmd;
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
extern const struct spi_flash_params spi_flash_params_table[];
|
||||
|
||||
/* Send a single-byte command to the device and read the response */
|
||||
int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
|
||||
|
||||
/*
|
||||
* Send a multi-byte command to the device and read the response. Used
|
||||
* for flash array reads, etc.
|
||||
*/
|
||||
int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
|
||||
size_t cmd_len, void *data, size_t data_len);
|
||||
|
||||
/*
|
||||
* Send a multi-byte command to the device followed by (optional)
|
||||
* data. Used for programming the flash array, etc.
|
||||
*/
|
||||
int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
|
||||
const void *data, size_t data_len);
|
||||
|
||||
|
||||
/* Flash erase(sectors) operation, support all possible erase commands */
|
||||
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len);
|
||||
|
||||
/* Lock stmicro spi flash region */
|
||||
int stm_lock(struct spi_flash *flash, u32 ofs, size_t len);
|
||||
|
||||
/* Unlock stmicro spi flash region */
|
||||
int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len);
|
||||
|
||||
/* Check if a stmicro spi flash region is completely locked */
|
||||
int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len);
|
||||
|
||||
/* Enable writing on the SPI flash */
|
||||
static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
|
||||
{
|
||||
return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0);
|
||||
}
|
||||
|
||||
/* Disable writing on the SPI flash */
|
||||
static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
|
||||
{
|
||||
return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used for spi_flash write operation
|
||||
* - SPI claim
|
||||
* - spi_flash_cmd_write_enable
|
||||
* - spi_flash_cmd_write
|
||||
* - spi_flash_cmd_wait_ready
|
||||
* - SPI release
|
||||
*/
|
||||
int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
|
||||
size_t cmd_len, const void *buf, size_t buf_len);
|
||||
|
||||
/*
|
||||
* Flash write operation, support all possible write commands.
|
||||
* Write the requested data out breaking it up into multiple write
|
||||
* commands as needed per the write size.
|
||||
*/
|
||||
int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
|
||||
size_t len, const void *buf);
|
||||
|
||||
/*
|
||||
* Same as spi_flash_cmd_read() except it also claims/releases the SPI
|
||||
* bus. Used as common part of the ->read() operation.
|
||||
*/
|
||||
int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
|
||||
size_t cmd_len, void *data, size_t data_len);
|
||||
|
||||
/* Flash read operation, support all possible read commands */
|
||||
int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
|
||||
size_t len, void *data);
|
||||
|
||||
#ifdef CONFIG_SPI_FLASH_MTD
|
||||
int spi_flash_mtd_register(struct spi_flash *flash);
|
||||
void spi_flash_mtd_unregister(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* spi_flash_scan - scan the SPI FLASH
|
||||
* @flash: the spi flash structure
|
||||
*
|
||||
* The drivers can use this fuction to scan the SPI FLASH.
|
||||
* In the scanning, it will try to get all the necessary information to
|
||||
* fill the spi_flash{}.
|
||||
*
|
||||
* Return: 0 for success, others for failure.
|
||||
*/
|
||||
int spi_flash_scan(struct spi_flash *flash);
|
||||
|
||||
#endif /* _SF_INTERNAL_H_ */
|
||||
104
u-boot/drivers/mtd/spi/sf_mtd.c
Normal file
104
u-boot/drivers/mtd/spi/sf_mtd.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/errno.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <spi_flash.h>
|
||||
|
||||
static struct mtd_info sf_mtd_info;
|
||||
static char sf_mtd_name[8];
|
||||
|
||||
static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct spi_flash *flash = mtd->priv;
|
||||
int err;
|
||||
|
||||
instr->state = MTD_ERASING;
|
||||
|
||||
err = spi_flash_erase(flash, instr->addr, instr->len);
|
||||
if (err) {
|
||||
instr->state = MTD_ERASE_FAILED;
|
||||
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_flash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct spi_flash *flash = mtd->priv;
|
||||
int err;
|
||||
|
||||
err = spi_flash_read(flash, from, len, buf);
|
||||
if (!err)
|
||||
*retlen = len;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int spi_flash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct spi_flash *flash = mtd->priv;
|
||||
int err;
|
||||
|
||||
err = spi_flash_write(flash, to, len, buf);
|
||||
if (!err)
|
||||
*retlen = len;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void spi_flash_mtd_sync(struct mtd_info *mtd)
|
||||
{
|
||||
}
|
||||
|
||||
static int spi_flash_mtd_number(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_MAX_FLASH_BANKS
|
||||
return CONFIG_SYS_MAX_FLASH_BANKS;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int spi_flash_mtd_register(struct spi_flash *flash)
|
||||
{
|
||||
memset(&sf_mtd_info, 0, sizeof(sf_mtd_info));
|
||||
sprintf(sf_mtd_name, "nor%d", spi_flash_mtd_number());
|
||||
|
||||
sf_mtd_info.name = sf_mtd_name;
|
||||
sf_mtd_info.type = MTD_NORFLASH;
|
||||
sf_mtd_info.flags = MTD_CAP_NORFLASH;
|
||||
sf_mtd_info.writesize = 1;
|
||||
sf_mtd_info.writebufsize = flash->page_size;
|
||||
|
||||
sf_mtd_info._erase = spi_flash_mtd_erase;
|
||||
sf_mtd_info._read = spi_flash_mtd_read;
|
||||
sf_mtd_info._write = spi_flash_mtd_write;
|
||||
sf_mtd_info._sync = spi_flash_mtd_sync;
|
||||
|
||||
sf_mtd_info.size = flash->size;
|
||||
sf_mtd_info.priv = flash;
|
||||
|
||||
/* Only uniform flash devices for now */
|
||||
sf_mtd_info.numeraseregions = 0;
|
||||
sf_mtd_info.erasesize = flash->sector_size;
|
||||
|
||||
return add_mtd_device(&sf_mtd_info);
|
||||
}
|
||||
|
||||
void spi_flash_mtd_unregister(void)
|
||||
{
|
||||
del_mtd_device(&sf_mtd_info);
|
||||
}
|
||||
147
u-boot/drivers/mtd/spi/sf_params.c
Normal file
147
u-boot/drivers/mtd/spi/sf_params.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPI flash Params table
|
||||
*
|
||||
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
|
||||
#include "sf_internal.h"
|
||||
|
||||
/* SPI/QSPI flash device params structure */
|
||||
const struct spi_flash_params spi_flash_params_table[] = {
|
||||
#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */
|
||||
{"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, RD_NORM, SECT_4K},
|
||||
{"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K},
|
||||
{"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K},
|
||||
{"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K},
|
||||
{"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, RD_NORM, SECT_4K},
|
||||
{"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K},
|
||||
{"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K},
|
||||
{"AT25DF321", 0x1f4701, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K},
|
||||
{"AT26DF081A", 0x1f4501, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_EON /* EON */
|
||||
{"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, RD_NORM, 0},
|
||||
{"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K},
|
||||
{"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, RD_NORM, 0},
|
||||
{"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, RD_NORM, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */
|
||||
{"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K},
|
||||
{"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_ISSI /* ISSI */
|
||||
{"IS25LP032", 0x9d6016, 0x0, 64 * 1024, 64, RD_NORM, 0},
|
||||
{"IS25LP064", 0x9d6017, 0x0, 64 * 1024, 128, RD_NORM, 0},
|
||||
{"IS25LP128", 0x9d6018, 0x0, 64 * 1024, 256, RD_NORM, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */
|
||||
{"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, RD_NORM, 0},
|
||||
{"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, RD_NORM, 0},
|
||||
{"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, RD_NORM, 0},
|
||||
{"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, RD_NORM, 0},
|
||||
{"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, RD_NORM, 0},
|
||||
{"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, RD_NORM, 0},
|
||||
{"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
|
||||
{"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP},
|
||||
{"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP},
|
||||
{"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
|
||||
{"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, RD_NORM, 0},
|
||||
{"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, RD_NORM, 0},
|
||||
{"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, RD_NORM, 0},
|
||||
{"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, RD_NORM, 0},
|
||||
{"S25FL116K", 0x014015, 0x0, 64 * 1024, 128, RD_NORM, 0},
|
||||
{"S25FL164K", 0x014017, 0x0140, 64 * 1024, 128, RD_NORM, 0},
|
||||
{"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, RD_FULL, WR_QPP},
|
||||
{"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, RD_FULL, WR_QPP},
|
||||
{"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, RD_FULL, WR_QPP},
|
||||
{"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, RD_FULL, WR_QPP},
|
||||
{"S25FL128S_256K", 0x012018, 0x4d00, 256 * 1024, 64, RD_FULL, WR_QPP},
|
||||
{"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, RD_FULL, WR_QPP},
|
||||
{"S25FL256S_256K", 0x010219, 0x4d00, 256 * 1024, 128, RD_FULL, WR_QPP},
|
||||
{"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, RD_FULL, WR_QPP},
|
||||
{"S25FS512S", 0x010220, 0x4D00, 128 * 1024, 512, RD_FULL, WR_QPP},
|
||||
{"S25FL512S_256K", 0x010220, 0x4d00, 256 * 1024, 256, RD_FULL, WR_QPP},
|
||||
{"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL, WR_QPP},
|
||||
{"S25FL512S_512K", 0x010220, 0x4f00, 256 * 1024, 256, RD_FULL, WR_QPP},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */
|
||||
{"M25P10", 0x202011, 0x0, 32 * 1024, 4, RD_NORM, 0},
|
||||
{"M25P20", 0x202012, 0x0, 64 * 1024, 4, RD_NORM, 0},
|
||||
{"M25P40", 0x202013, 0x0, 64 * 1024, 8, RD_NORM, 0},
|
||||
{"M25P80", 0x202014, 0x0, 64 * 1024, 16, RD_NORM, 0},
|
||||
{"M25P16", 0x202015, 0x0, 64 * 1024, 32, RD_NORM, 0},
|
||||
{"M25PE16", 0x208015, 0x1000, 64 * 1024, 32, RD_NORM, 0},
|
||||
{"M25PX16", 0x207115, 0x1000, 64 * 1024, 32, RD_EXTN, 0},
|
||||
{"M25P32", 0x202016, 0x0, 64 * 1024, 64, RD_NORM, 0},
|
||||
{"M25P64", 0x202017, 0x0, 64 * 1024, 128, RD_NORM, 0},
|
||||
{"M25P128", 0x202018, 0x0, 256 * 1024, 64, RD_NORM, 0},
|
||||
{"M25PX64", 0x207117, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K},
|
||||
{"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
|
||||
{"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
|
||||
{"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K},
|
||||
{"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K},
|
||||
{"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K},
|
||||
{"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_SST /* SST */
|
||||
{"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K},
|
||||
{"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR},
|
||||
{"SST25WF040B", 0x621613, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K},
|
||||
{"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, RD_NORM, SECT_4K | SST_WR},
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */
|
||||
{"W25P80", 0xef2014, 0x0, 64 * 1024, 16, RD_NORM, 0},
|
||||
{"W25P16", 0xef2015, 0x0, 64 * 1024, 32, RD_NORM, 0},
|
||||
{"W25P32", 0xef2016, 0x0, 64 * 1024, 64, RD_NORM, 0},
|
||||
{"W25X40", 0xef3013, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K},
|
||||
{"W25X16", 0xef3015, 0x0, 64 * 1024, 32, RD_NORM, SECT_4K},
|
||||
{"W25X32", 0xef3016, 0x0, 64 * 1024, 64, RD_NORM, SECT_4K},
|
||||
{"W25X64", 0xef3017, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K},
|
||||
{"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
|
||||
{"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K},
|
||||
#endif
|
||||
{}, /* Empty entry to terminate the list */
|
||||
/*
|
||||
* Note:
|
||||
* Below paired flash devices has similar spi_flash params.
|
||||
* (S25FL129P_64K, S25FL128S_64K)
|
||||
* (W25Q80BL, W25Q80BV)
|
||||
* (W25Q16CL, W25Q16DV)
|
||||
* (W25Q32BV, W25Q32FV_SPI)
|
||||
* (W25Q64CV, W25Q64FV_SPI)
|
||||
* (W25Q128BV, W25Q128FV_SPI)
|
||||
* (W25Q32DW, W25Q32FV_QPI)
|
||||
* (W25Q64DW, W25Q64FV_QPI)
|
||||
* (W25Q128FW, W25Q128FV_QPI)
|
||||
*/
|
||||
};
|
||||
179
u-boot/drivers/mtd/spi/sf_probe.c
Normal file
179
u-boot/drivers/mtd/spi/sf_probe.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* SPI flash probing
|
||||
*
|
||||
* Copyright (C) 2008 Atmel Corporation
|
||||
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
|
||||
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
|
||||
#include "sf_internal.h"
|
||||
|
||||
/**
|
||||
* spi_flash_probe_slave() - Probe for a SPI flash device on a bus
|
||||
*
|
||||
* @flashp: Pointer to place to put flash info, which may be NULL if the
|
||||
* space should be allocated
|
||||
*/
|
||||
static int spi_flash_probe_slave(struct spi_flash *flash)
|
||||
{
|
||||
struct spi_slave *spi = flash->spi;
|
||||
int ret;
|
||||
|
||||
/* Setup spi_slave */
|
||||
if (!spi) {
|
||||
printf("SF: Failed to set up slave\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Claim spi bus */
|
||||
ret = spi_claim_bus(spi);
|
||||
if (ret) {
|
||||
debug("SF: Failed to claim SPI bus: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_flash_scan(flash);
|
||||
if (ret)
|
||||
goto err_read_id;
|
||||
|
||||
#ifdef CONFIG_SPI_FLASH_MTD
|
||||
ret = spi_flash_mtd_register(flash);
|
||||
#endif
|
||||
|
||||
err_read_id:
|
||||
spi_release_bus(spi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_SPI_FLASH
|
||||
static struct spi_flash *spi_flash_probe_tail(struct spi_slave *bus)
|
||||
{
|
||||
struct spi_flash *flash;
|
||||
|
||||
/* Allocate space if needed (not used by sf-uclass */
|
||||
flash = calloc(1, sizeof(*flash));
|
||||
if (!flash) {
|
||||
debug("SF: Failed to allocate spi_flash\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
flash->spi = bus;
|
||||
if (spi_flash_probe_slave(flash)) {
|
||||
spi_free_slave(bus);
|
||||
free(flash);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return flash;
|
||||
}
|
||||
|
||||
struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int spi_mode)
|
||||
{
|
||||
struct spi_slave *bus;
|
||||
|
||||
bus = spi_setup_slave(busnum, cs, max_hz, spi_mode);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
return spi_flash_probe_tail(bus);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_SPI_FLASH
|
||||
struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node,
|
||||
int spi_node)
|
||||
{
|
||||
struct spi_slave *bus;
|
||||
|
||||
bus = spi_setup_slave_fdt(blob, slave_node, spi_node);
|
||||
if (!bus)
|
||||
return NULL;
|
||||
return spi_flash_probe_tail(bus);
|
||||
}
|
||||
#endif
|
||||
|
||||
void spi_flash_free(struct spi_flash *flash)
|
||||
{
|
||||
#ifdef CONFIG_SPI_FLASH_MTD
|
||||
spi_flash_mtd_unregister();
|
||||
#endif
|
||||
spi_free_slave(flash->spi);
|
||||
free(flash);
|
||||
}
|
||||
|
||||
#else /* defined CONFIG_DM_SPI_FLASH */
|
||||
|
||||
static int spi_flash_std_read(struct udevice *dev, u32 offset, size_t len,
|
||||
void *buf)
|
||||
{
|
||||
struct spi_flash *flash = dev_get_uclass_priv(dev);
|
||||
|
||||
return spi_flash_cmd_read_ops(flash, offset, len, buf);
|
||||
}
|
||||
|
||||
static int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len,
|
||||
const void *buf)
|
||||
{
|
||||
struct spi_flash *flash = dev_get_uclass_priv(dev);
|
||||
|
||||
#if defined(CONFIG_SPI_FLASH_SST)
|
||||
if (flash->flags & SNOR_F_SST_WR) {
|
||||
if (flash->spi->mode & SPI_TX_BYTE)
|
||||
return sst_write_bp(flash, offset, len, buf);
|
||||
else
|
||||
return sst_write_wp(flash, offset, len, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
return spi_flash_cmd_write_ops(flash, offset, len, buf);
|
||||
}
|
||||
|
||||
static int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len)
|
||||
{
|
||||
struct spi_flash *flash = dev_get_uclass_priv(dev);
|
||||
|
||||
return spi_flash_cmd_erase_ops(flash, offset, len);
|
||||
}
|
||||
|
||||
static int spi_flash_std_probe(struct udevice *dev)
|
||||
{
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev);
|
||||
struct spi_flash *flash;
|
||||
|
||||
flash = dev_get_uclass_priv(dev);
|
||||
flash->dev = dev;
|
||||
flash->spi = slave;
|
||||
debug("%s: slave=%p, cs=%d\n", __func__, slave, plat->cs);
|
||||
return spi_flash_probe_slave(flash);
|
||||
}
|
||||
|
||||
static const struct dm_spi_flash_ops spi_flash_std_ops = {
|
||||
.read = spi_flash_std_read,
|
||||
.write = spi_flash_std_write,
|
||||
.erase = spi_flash_std_erase,
|
||||
};
|
||||
|
||||
static const struct udevice_id spi_flash_std_ids[] = {
|
||||
{ .compatible = "spi-flash" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(spi_flash_std) = {
|
||||
.name = "spi_flash_std",
|
||||
.id = UCLASS_SPI_FLASH,
|
||||
.of_match = spi_flash_std_ids,
|
||||
.probe = spi_flash_std_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct spi_flash),
|
||||
.ops = &spi_flash_std_ops,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_DM_SPI_FLASH */
|
||||
1268
u-boot/drivers/mtd/spi/spi_flash.c
Normal file
1268
u-boot/drivers/mtd/spi/spi_flash.c
Normal file
File diff suppressed because it is too large
Load Diff
123
u-boot/drivers/mtd/spi/spi_spl_load.c
Normal file
123
u-boot/drivers/mtd/spi/spi_spl_load.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2011 OMICRON electronics GmbH
|
||||
*
|
||||
* based on drivers/mtd/nand/nand_spl_load.c
|
||||
*
|
||||
* Copyright (C) 2011
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
#include <errno.h>
|
||||
#include <spl.h>
|
||||
|
||||
#ifdef CONFIG_SPL_OS_BOOT
|
||||
/*
|
||||
* Load the kernel, check for a valid header we can parse, and if found load
|
||||
* the kernel and then device tree.
|
||||
*/
|
||||
static int spi_load_image_os(struct spi_flash *flash,
|
||||
struct image_header *header)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Read for a header, parse or error out. */
|
||||
spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, 0x40,
|
||||
(void *)header);
|
||||
|
||||
if (image_get_magic(header) != IH_MAGIC)
|
||||
return -1;
|
||||
|
||||
err = spl_parse_image_header(header);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS,
|
||||
spl_image.size, (void *)spl_image.load_addr);
|
||||
|
||||
/* Read device tree. */
|
||||
spi_flash_read(flash, CONFIG_SYS_SPI_ARGS_OFFS,
|
||||
CONFIG_SYS_SPI_ARGS_SIZE,
|
||||
(void *)CONFIG_SYS_SPL_ARGS_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ulong spl_spi_fit_read(struct spl_load_info *load, ulong sector,
|
||||
ulong count, void *buf)
|
||||
{
|
||||
struct spi_flash *flash = load->dev;
|
||||
ulong ret;
|
||||
|
||||
ret = spi_flash_read(flash, sector, count, buf);
|
||||
if (!ret)
|
||||
return count;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* The main entry for SPI booting. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from SPI into SDRAM and starts it from there.
|
||||
*/
|
||||
int spl_spi_load_image(void)
|
||||
{
|
||||
int err = 0;
|
||||
struct spi_flash *flash;
|
||||
struct image_header *header;
|
||||
|
||||
/*
|
||||
* Load U-Boot image from SPI flash into RAM
|
||||
*/
|
||||
|
||||
flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS,
|
||||
CONFIG_SF_DEFAULT_CS,
|
||||
CONFIG_SF_DEFAULT_SPEED,
|
||||
CONFIG_SF_DEFAULT_MODE);
|
||||
if (!flash) {
|
||||
puts("SPI probe failed.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* use CONFIG_SYS_TEXT_BASE as temporary storage area */
|
||||
header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);
|
||||
|
||||
#ifdef CONFIG_SPL_OS_BOOT
|
||||
if (spl_start_uboot() || spi_load_image_os(flash, header))
|
||||
#endif
|
||||
{
|
||||
/* Load u-boot, mkimage header is 64 bytes. */
|
||||
err = spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40,
|
||||
(void *)header);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) {
|
||||
struct spl_load_info load;
|
||||
|
||||
debug("Found FIT\n");
|
||||
load.dev = flash;
|
||||
load.priv = NULL;
|
||||
load.filename = NULL;
|
||||
load.bl_len = 1;
|
||||
load.read = spl_spi_fit_read;
|
||||
err = spl_load_simple_fit(&load,
|
||||
CONFIG_SYS_SPI_U_BOOT_OFFS,
|
||||
header);
|
||||
} else {
|
||||
err = spl_parse_image_header(header);
|
||||
if (err)
|
||||
return err;
|
||||
err = spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS,
|
||||
spl_image.size,
|
||||
(void *)spl_image.load_addr);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
Reference in New Issue
Block a user