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:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

View 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"

View 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

View 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)();
}

View 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,
};

View 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),
};

View 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);
}

View 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,
};

View 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_ */

View 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);
}

View 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)
*/
};

View 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 */

File diff suppressed because it is too large Load Diff

View 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;
}