Merge branch 'spi' of git://git.denx.de/u-boot-x86

master
Tom Rini 11 years ago
commit 65b7fe28a1
  1. 2
      arch/sandbox/cpu/os.c
  2. 17
      arch/sandbox/cpu/start.c
  3. 8
      arch/sandbox/include/asm/config.h
  4. 23
      arch/sandbox/include/asm/getopt.h
  5. 4
      arch/sandbox/include/asm/sections.h
  6. 58
      arch/sandbox/include/asm/spi.h
  7. 9
      arch/sandbox/include/asm/state.h
  8. 54
      board/sandbox/sandbox/README.sandbox
  9. 64
      doc/SPI/README.sandbox-spi
  10. 92
      doc/device-tree-bindings/spi/spi-bus.txt
  11. 3
      drivers/misc/cros_ec_spi.c
  12. 1
      drivers/mtd/spi/Makefile
  13. 483
      drivers/mtd/spi/sandbox.c
  14. 1
      drivers/mtd/spi/sf_internal.h
  15. 28
      drivers/mtd/spi/sf_probe.c
  16. 1
      drivers/spi/Makefile
  17. 10
      drivers/spi/exynos_spi.c
  18. 204
      drivers/spi/sandbox_spi.c
  19. 19
      drivers/spi/spi.c
  20. 1
      include/configs/exynos5250-dt.h
  21. 10
      include/configs/sandbox.h
  22. 23
      include/spi.h
  23. 13
      include/spi_flash.h

@ -161,7 +161,7 @@ static struct option *long_opts;
int os_parse_args(struct sandbox_state *state, int argc, char *argv[])
{
struct sb_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
size_t num_options = __u_boot_sandbox_option_count();
size_t i;

@ -13,7 +13,7 @@
int sandbox_early_getopt_check(void)
{
struct sandbox_state *state = state_get_current();
struct sb_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
size_t num_options = __u_boot_sandbox_option_count();
size_t i;
int max_arg_len, max_noarg_len;
@ -40,7 +40,7 @@ int sandbox_early_getopt_check(void)
max_noarg_len = max_arg_len + 7;
for (i = 0; i < num_options; ++i) {
struct sb_cmdline_option *opt = sb_opt[i];
struct sandbox_cmdline_option *opt = sb_opt[i];
/* first output the short flag if it has one */
if (opt->flag_short >= 0x100)
@ -61,12 +61,12 @@ int sandbox_early_getopt_check(void)
os_exit(0);
}
static int sb_cmdline_cb_help(struct sandbox_state *state, const char *arg)
static int sandbox_cmdline_cb_help(struct sandbox_state *state, const char *arg)
{
/* just flag to sandbox_early_getopt_check to show usage */
return 1;
}
SB_CMDLINE_OPT_SHORT(help, 'h', 0, "Display help");
SANDBOX_CMDLINE_OPT_SHORT(help, 'h', 0, "Display help");
int sandbox_main_loop_init(void)
{
@ -81,19 +81,20 @@ int sandbox_main_loop_init(void)
return 0;
}
static int sb_cmdline_cb_command(struct sandbox_state *state, const char *arg)
static int sandbox_cmdline_cb_command(struct sandbox_state *state,
const char *arg)
{
state->cmd = arg;
return 0;
}
SB_CMDLINE_OPT_SHORT(command, 'c', 1, "Execute U-Boot command");
SANDBOX_CMDLINE_OPT_SHORT(command, 'c', 1, "Execute U-Boot command");
static int sb_cmdline_cb_fdt(struct sandbox_state *state, const char *arg)
static int sandbox_cmdline_cb_fdt(struct sandbox_state *state, const char *arg)
{
state->fdt_fname = arg;
return 0;
}
SB_CMDLINE_OPT_SHORT(fdt, 'd', 1, "Specify U-Boot's control FDT");
SANDBOX_CMDLINE_OPT_SHORT(fdt, 'd', 1, "Specify U-Boot's control FDT");
int main(int argc, char *argv[])
{

@ -9,4 +9,12 @@
#define CONFIG_SANDBOX_ARCH
/* Used by drivers/spi/sandbox_spi.c and arch/sandbox/include/asm/state.h */
#ifndef CONFIG_SANDBOX_SPI_MAX_BUS
#define CONFIG_SANDBOX_SPI_MAX_BUS 1
#endif
#ifndef CONFIG_SANDBOX_SPI_MAX_CS
#define CONFIG_SANDBOX_SPI_MAX_CS 10
#endif
#endif

@ -18,7 +18,7 @@ struct sandbox_state;
* consumer code should focus on the macros below and
* the callback function.
*/
struct sb_cmdline_option {
struct sandbox_cmdline_option {
/* The long flag name: "help" for "--help" */
const char *flag;
/* The (optional) short flag name: "h" for "-h" */
@ -35,18 +35,19 @@ struct sb_cmdline_option {
* Internal macro to expand the lower macros into the necessary
* magic junk that makes this all work.
*/
#define _SB_CMDLINE_OPT(f, s, ha, h) \
static struct sb_cmdline_option sb_cmdline_option_##f = { \
#define _SANDBOX_CMDLINE_OPT(f, s, ha, h) \
static struct sandbox_cmdline_option sandbox_cmdline_option_##f = { \
.flag = #f, \
.flag_short = s, \
.help = h, \
.has_arg = ha, \
.callback = sb_cmdline_cb_##f, \
.callback = sandbox_cmdline_cb_##f, \
}; \
/* Ppointer to the struct in a special section for the linker script */ \
static __attribute__((section(".u_boot_sandbox_getopt"), used)) \
struct sb_cmdline_option *sb_cmdline_option_##f##_ptr = \
&sb_cmdline_option_##f
struct sandbox_cmdline_option \
*sandbox_cmdline_option_##f##_ptr = \
&sandbox_cmdline_option_##f
/**
* Macros for end code to declare new command line flags.
@ -56,16 +57,16 @@ struct sb_cmdline_option {
* @param h The help string displayed when showing --help
*
* This invocation:
* SB_CMDLINE_OPT(foo, 0, "The foo arg");
* SANDBOX_CMDLINE_OPT(foo, 0, "The foo arg");
* Will create a new flag named "--foo" (no short option) that takes
* no argument. If the user specifies "--foo", then the callback func
* sb_cmdline_cb_foo() will automatically be called.
* sandbox_cmdline_cb_foo() will automatically be called.
*/
#define SB_CMDLINE_OPT(f, ha, h) _SB_CMDLINE_OPT(f, 0, ha, h)
#define SANDBOX_CMDLINE_OPT(f, ha, h) _SANDBOX_CMDLINE_OPT(f, 0, ha, h)
/*
* Same as above, but @s is used to specify a short flag e.g.
* SB_CMDLINE_OPT(foo, 'f', 0, "The foo arg");
* SANDBOX_CMDLINE_OPT(foo, 'f', 0, "The foo arg");
*/
#define SB_CMDLINE_OPT_SHORT(f, s, ha, h) _SB_CMDLINE_OPT(f, s, ha, h)
#define SANDBOX_CMDLINE_OPT_SHORT(f, s, ha, h) _SANDBOX_CMDLINE_OPT(f, s, ha, h)
#endif

@ -11,9 +11,9 @@
#include <asm-generic/sections.h>
struct sb_cmdline_option;
struct sandbox_cmdline_option;
extern struct sb_cmdline_option *__u_boot_sandbox_option_start[],
extern struct sandbox_cmdline_option *__u_boot_sandbox_option_start[],
*__u_boot_sandbox_option_end[];
static inline size_t __u_boot_sandbox_option_count(void)

@ -0,0 +1,58 @@
/*
* Simulate a SPI port and clients (see README.sandbox for details)
*
* 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.
*/
#ifndef __ASM_SPI_H__
#define __ASM_SPI_H__
#include <linux/types.h>
/*
* The interface between the SPI bus and the SPI client. The bus will
* instantiate a client, and that then call into it via these entry
* points. These should be enough for the client to emulate the SPI
* device just like the real hardware.
*/
struct sandbox_spi_emu_ops {
/* The bus wants to instantiate a new client, so setup everything */
int (*setup)(void **priv, const char *spec);
/* The bus is done with us, so break things down */
void (*free)(void *priv);
/* The CS has been "activated" -- we won't worry about low/high */
void (*cs_activate)(void *priv);
/* The CS has been "deactivated" -- we won't worry about low/high */
void (*cs_deactivate)(void *priv);
/* The client is rx-ing bytes from the bus, so it should tx some */
int (*xfer)(void *priv, const u8 *rx, u8 *tx, uint bytes);
};
/*
* 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 inline void sandbox_spi_tristate(u8 *buf, uint len)
{
/* XXX: make this into a user config option ? */
memset(buf, 0xff, len);
}
/*
* Extract the bus/cs from the spi spec and return the start of the spi
* client spec. If the bus/cs are invalid for the current config, then
* it returns NULL.
*
* Example: arg="0:1:foo" will set bus to 0, cs to 1, and return "foo"
*/
const char *sandbox_spi_parse_spec(const char *arg, unsigned long *bus,
unsigned long *cs);
#endif

@ -15,6 +15,11 @@ enum exit_type_id {
STATE_EXIT_POWER_OFF,
};
struct sandbox_spi_info {
const char *spec;
const struct sandbox_spi_emu_ops *ops;
};
/* The complete state of the test system */
struct sandbox_state {
const char *cmd; /* Command to execute */
@ -23,6 +28,10 @@ struct sandbox_state {
const char *parse_err; /* Error to report from parsing */
int argc; /* Program arguments */
char **argv;
/* Pointer to information for each SPI bus/cs */
struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS]
[CONFIG_SANDBOX_SPI_MAX_CS];
};
/**

@ -31,6 +31,60 @@ the console. It does not set the terminal into raw mode, so cursor keys and
history will not work yet.
SPI Emulation
-------------
Sandbox supports SPI and SPI flash emulation.
This is controlled by the spi_sf argument, the format of which is:
bus:cs:device:file
bus - SPI bus number
cs - SPI chip select number
device - SPI device emulation name
file - File on disk containing the data
For example:
dd if=/dev/zero of=spi.bin bs=1M count=4
./u-boot --spi_sf 0:0:M25P16:spi.bin
With this setup you can issue SPI flash commands as normal:
=>sf probe
SF: Detected M25P16 with page size 64 KiB, total 2 MiB
=>sf read 0 0 10000
SF: 65536 bytes @ 0x0 Read: OK
=>
Since this is a full SPI emulation (rather than just flash), you can
also use low-level SPI commands:
=>sspi 0:0 32 9f
FF202015
This is issuing a READ_ID command and getting back 20 (ST Micro) part
0x2015 (the M25P16).
Drivers are connected to a particular bus/cs using sandbox's state
structure (see the 'spi' member). A set of operations must be provided
for each driver.
Configuration settings for the curious are:
CONFIG_SANDBOX_SPI_MAX_BUS
The maximum number of SPI buses supported by the driver (default 1).
CONFIG_SANDBOX_SPI_MAX_CS
The maximum number of chip selects supported by the driver
(default 10).
CONFIG_SPI_IDLE_VAL
The idle value on the SPI bus
Tests
-----

@ -0,0 +1,64 @@
Sandbox SPI/SPI Flash Implementation
====================================
U-Boot supports SPI and SPI flash emuation in sandbox. This must be enabled
using the --spi_sf paramter when starting U-Boot.
For example:
$ make O=sandbox sandbox_config
$ make O=sandbox
$ ./sandbox/u-boot --spi_sf 0:0:W25Q128:b/chromeos_peach/out/image.bin
The four parameters to spi_sf are:
SPI bus number (typically 0)
SPI chip select number (typically 0)
SPI chip to emulate
File containing emulated data
Supported chips are W25Q16 (2MB), W25Q32 (4MB) and W25Q128 (16MB). Once
U-Boot it started you can use 'sf' commands as normal. For example:
$ ./b/sandbox/u-boot --spi_sf 0:0:W25Q128:b/chromeos_peach/out/image.bin \
-c "sf probe; sf test 0 100000; sf read 0 1000 1000; \
sf erase 1000 1000; sf write 0 1000 1000"
U-Boot 2013.10-00237-gd4e0fdb (Nov 07 2013 - 20:08:15)
DRAM: 128 MiB
Using default environment
In: serial
Out: serial
Err: serial
SF: Detected W25Q128BV with page size 256 Bytes, erase size 4 KiB, total 16 MiB
SPI flash test:
0 erase: 1 ticks, 1024000 KiB/s 8192.000 Mbps
1 check: 2 ticks, 512000 KiB/s 4096.000 Mbps
2 write: 6 ticks, 170666 KiB/s 1365.328 Mbps
3 read: 0 ticks, 1048576000 KiB/s -201326.-592 Mbps
Test passed
0 erase: 1 ticks, 1024000 KiB/s 8192.000 Mbps
1 check: 2 ticks, 512000 KiB/s 4096.000 Mbps
2 write: 6 ticks, 170666 KiB/s 1365.328 Mbps
3 read: 0 ticks, 1048576000 KiB/s -201326.-592 Mbps
SF: 4096 bytes @ 0x1000 Read: OK
SF: 4096 bytes @ 0x1000 Erased: OK
SF: 4096 bytes @ 0x1000 Written: OK
Since the SPI bus is fully implemented as well as the SPI flash connected to
it, you can also use low-level SPI commands to access the flash. For example
this reads the device ID from the emulated chip:
=> sspi 0 32 9f
FFEF4018
Simon Glass
sjg@chromium.org
7/11/2013
Note that the sandbox SPI implementation was written by Mike Frysinger
<vapier@gentoo.org>.

@ -0,0 +1,92 @@
SPI (Serial Peripheral Interface) busses
SPI busses can be described with a node for the SPI master device
and a set of child nodes for each SPI slave on the bus. For this
discussion, it is assumed that the system's SPI controller is in
SPI master mode. This binding does not describe SPI controllers
in slave mode.
The SPI master node requires the following properties:
- #address-cells - number of cells required to define a chip select
address on the SPI bus.
- #size-cells - should be zero.
- compatible - name of SPI bus controller following generic names
recommended practice.
- cs-gpios - (optional) gpios chip select.
No other properties are required in the SPI bus node. It is assumed
that a driver for an SPI bus device will understand that it is an SPI bus.
However, the binding does not attempt to define the specific method for
assigning chip select numbers. Since SPI chip select configuration is
flexible and non-standardized, it is left out of this binding with the
assumption that board specific platform code will be used to manage
chip selects. Individual drivers can define additional properties to
support describing the chip select layout.
Optional property:
- num-cs : total number of chipselects
If cs-gpios is used the number of chip select will automatically increased
with max(cs-gpios > hw cs)
So if for example the controller has 2 CS lines, and the cs-gpios
property looks like this:
cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>;
Then it should be configured so that num_chipselect = 4 with the
following mapping:
cs0 : &gpio1 0 0
cs1 : native
cs2 : &gpio1 1 0
cs3 : &gpio1 2 0
SPI slave nodes must be children of the SPI master node and can
contain the following properties.
- reg - (required) chip select address of device.
- compatible - (required) name of SPI device following generic names
recommended practice
- spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz
- spi-cpol - (optional) Empty property indicating device requires
inverse clock polarity (CPOL) mode
- spi-cpha - (optional) Empty property indicating device requires
shifted clock phase (CPHA) mode
- spi-cs-high - (optional) Empty property indicating device requires
chip select active high
- spi-3wire - (optional) Empty property indicating device requires
3-wire mode.
- spi-tx-bus-width - (optional) The bus width(number of data wires) that
used for MOSI. Defaults to 1 if not present.
- spi-rx-bus-width - (optional) The bus width(number of data wires) that
used for MISO. Defaults to 1 if not present.
Some SPI controllers and devices support Dual and Quad SPI transfer mode.
It allows data in SPI system transfered in 2 wires(DUAL) or 4 wires(QUAD).
Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is
only 1(SINGLE), 2(DUAL) and 4(QUAD).
Dual/Quad mode is not allowed when 3-wire mode is used.
If a gpio chipselect is used for the SPI slave the gpio number will be passed
via the cs_gpio
SPI example for an MPC5200 SPI bus:
spi@f00 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";
reg = <0xf00 0x20>;
interrupts = <2 13 0 2 14 0>;
interrupt-parent = <&mpc5200_pic>;
ethernet-switch@0 {
compatible = "micrel,ks8995m";
spi-max-frequency = <1000000>;
reg = <0>;
};
codec@1 {
compatible = "ti,tlv320aic26";
spi-max-frequency = <100000>;
reg = <1>;
};
};

@ -135,8 +135,7 @@ int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob)
*/
int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob)
{
dev->spi = spi_setup_slave_fdt(blob, dev->parent_node,
dev->cs, dev->max_frequency, 0);
dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, dev->node);
if (!dev->spi) {
debug("%s: Could not setup SPI slave\n", __func__);
return -1;

@ -13,4 +13,5 @@ endif
obj-$(CONFIG_CMD_SF) += sf.o
obj-$(CONFIG_SPI_FLASH) += sf_probe.o sf_ops.o
obj-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o
obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o
obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o

@ -0,0 +1,483 @@
/*
* 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 <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>
/*
* 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*/
};
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",
};
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
struct sandbox_spi_flash_erase_commands {
u8 cmd;
u32 size;
};
#define IDCODE_LEN 5
#define MAX_ERASE_CMDS 3
struct sandbox_spi_flash_data {
const char *name;
u8 idcode[IDCODE_LEN];
u32 size;
const struct sandbox_spi_flash_erase_commands
erase_cmds[MAX_ERASE_CMDS];
};
/* Structure describing all the flashes we know how to emulate */
static const struct sandbox_spi_flash_data sandbox_sf_flashes[] = {
{
"M25P16", { 0x20, 0x20, 0x15 }, (2 << 20),
{ /* erase commands */
{ 0xd8, (64 << 10), }, /* sector */
{ 0xc7, (2 << 20), }, /* bulk */
},
},
{
"W25Q32", { 0xef, 0x40, 0x16 }, (4 << 20),
{ /* erase commands */
{ 0x20, (4 << 10), }, /* 4KB */
{ 0xd8, (64 << 10), }, /* sector */
{ 0xc7, (4 << 20), }, /* bulk */
},
},
{
"W25Q128", { 0xef, 0x40, 0x18 }, (16 << 20),
{ /* erase commands */
{ 0x20, (4 << 10), }, /* 4KB */
{ 0xd8, (64 << 10), }, /* sector */
{ 0xc7, (16 << 20), }, /* bulk */
},
},
};
/* Used to quickly bulk erase backing store */
static u8 sandbox_sf_0xff[0x1000];
/* Internal state data for each SPI flash */
struct sandbox_spi_flash {
/*
* 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;
const void *cmd_data;
/* 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 sandbox_spi_flash_data *data;
/* The file on disk to serv up data from */
int fd;
};
static int sandbox_sf_setup(void **priv, const char *spec)
{
/* spec = idcode:file */
struct sandbox_spi_flash *sbsf;
const char *file;
size_t i, len, idname_len;
const struct sandbox_spi_flash_data *data;
file = strchr(spec, ':');
if (!file) {
printf("sandbox_sf: unable to parse file\n");
goto error;
}
idname_len = file - spec;
++file;
for (i = 0; i < ARRAY_SIZE(sandbox_sf_flashes); ++i) {
data = &sandbox_sf_flashes[i];
len = strlen(data->name);
if (idname_len != len)
continue;
if (!memcmp(spec, data->name, len))
break;
}
if (i == ARRAY_SIZE(sandbox_sf_flashes)) {
printf("sandbox_sf: unknown flash '%*s'\n", (int)idname_len,
spec);
goto error;
}
if (sandbox_sf_0xff[0] == 0x00)
memset(sandbox_sf_0xff, 0xff, sizeof(sandbox_sf_0xff));
sbsf = calloc(sizeof(*sbsf), 1);
if (!sbsf) {
printf("sandbox_sf: out of memory\n");
goto error;
}
sbsf->fd = os_open(file, 02);
if (sbsf->fd == -1) {
free(sbsf);
printf("sandbox_sf: unable to open file '%s'\n", file);
goto error;
}
sbsf->data = data;
*priv = sbsf;
return 0;
error:
return 1;
}
static void sandbox_sf_free(void *priv)
{
struct sandbox_spi_flash *sbsf = priv;
os_close(sbsf->fd);
free(sbsf);
}
static void sandbox_sf_cs_activate(void *priv)
{
struct sandbox_spi_flash *sbsf = priv;
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(void *priv)
{
debug("sandbox_sf: CS deactivated; cmd done processing!\n");
}
/* 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 */
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:
state_addr:
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;
default: {
size_t i;
/* handle erase commands first */
for (i = 0; i < MAX_ERASE_CMDS; ++i) {
const struct sandbox_spi_flash_erase_commands *
erase_cmd = &sbsf->data->erase_cmds[i];
if (erase_cmd->cmd == 0x00)
continue;
if (sbsf->cmd != erase_cmd->cmd)
continue;
sbsf->cmd_data = erase_cmd;
goto state_addr;
}
debug(" cmd unknown: %#x\n", sbsf->cmd);
return 1;
}
}
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, 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(void *priv, const u8 *rx, u8 *tx,
uint bytes)
{
struct sandbox_spi_flash *sbsf = priv;
uint cnt, pos = 0;
int ret;
debug("sandbox_sf: state:%x(%s) bytes:%u\n", sbsf->state,
sandbox_sf_state_name(sbsf->state), bytes);
if (sbsf->state == SF_CMD) {
/* Figure out the initial state */
if (sandbox_sf_process_cmd(sbsf, rx, tx))
return 1;
++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)
id = sbsf->data->idcode[sbsf->off];
else
id = 0;
debug("%02x\n", 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);
sandbox_spi_tristate(&tx[pos++], 1);
/* 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 1;
}
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);
ret = os_read(sbsf->fd, tx + pos, cnt);
if (ret < 0) {
puts("sandbox_spi: os_read() failed\n");
return 1;
}
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:
/*
* 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);
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 1;
}
pos += ret;
sbsf->status &= ~STAT_WEL;
break;
case SF_ERASE:
case_sf_erase: {
const struct sandbox_spi_flash_erase_commands *
erase_cmd = sbsf->cmd_data;
if (!(sbsf->status & STAT_WEL)) {
puts("sandbox_sf: write enable not set before erase\n");
goto done;
}
/* verify address is aligned */
if (sbsf->off & (erase_cmd->size - 1)) {
debug(" sector erase: cmd:%#x needs align:%#x, but we got %#x\n",
erase_cmd->cmd, erase_cmd->size,
sbsf->off);
sbsf->status &= ~STAT_WEL;
goto done;
}
debug(" sector erase addr: %u\n", sbsf->off);
cnt = bytes - pos;
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, erase_cmd->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:
return pos == bytes ? 0 : 1;
}
static const struct sandbox_spi_emu_ops sandbox_sf_ops = {
.setup = sandbox_sf_setup,
.free = sandbox_sf_free,
.cs_activate = sandbox_sf_cs_activate,
.cs_deactivate = sandbox_sf_cs_deactivate,
.xfer = sandbox_sf_xfer,
};
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].ops = &sandbox_sf_ops;
state->spi[bus][cs].spec = spec;
return 0;
}
SANDBOX_CMDLINE_OPT(spi_sf, 1, "connect a SPI flash: <bus>:<cs>:<id>:<file>");

@ -28,6 +28,7 @@
#define CMD_PAGE_PROGRAM 0x02
#define CMD_WRITE_DISABLE 0x04
#define CMD_READ_STATUS 0x05
#define CMD_READ_STATUS1 0x35
#define CMD_WRITE_ENABLE 0x06
#define CMD_READ_CONFIG 0x35
#define CMD_FLAG_STATUS 0x70

@ -13,6 +13,7 @@
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
#include <asm/io.h>
#include "sf_internal.h"
@ -279,22 +280,19 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
debug("%s: Memory map must cover entire device\n", __func__);
return -1;
}
flash->memory_map = (void *)addr;
flash->memory_map = map_sysmem(addr, size);
return 0;
}
#endif /* CONFIG_OF_CONTROL */
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi)
{
struct spi_slave *spi;
struct spi_flash *flash = NULL;
u8 idcode[5];
int ret;
/* Setup spi_slave */
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
if (!spi) {
printf("SF: Failed to set up slave\n");
return NULL;
@ -358,6 +356,26 @@ err_claim_bus:
return NULL;
}
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
struct spi_slave *spi;
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
return spi_flash_probe_slave(spi);
}
#ifdef CONFIG_OF_SPI_FLASH
struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node,
int spi_node)
{
struct spi_slave *spi;
spi = spi_setup_slave_fdt(blob, slave_node, spi_node);
return spi_flash_probe_slave(spi);
}
#endif
void spi_flash_free(struct spi_flash *flash)
{
spi_free_slave(flash->spi);

@ -27,6 +27,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
obj-$(CONFIG_MXS_SPI) += mxs_spi.o
obj-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o
obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
obj-$(CONFIG_SOFT_SPI) += soft_spi.o
obj-$(CONFIG_SH_SPI) += sh_spi.o
obj-$(CONFIG_FSL_ESPI) += fsl_espi.o

@ -529,18 +529,18 @@ static int process_nodes(const void *blob, int node_list[], int count)
* @param node SPI peripheral node to use
* @return 0 if ok, -1 on error
*/
struct spi_slave *spi_setup_slave_fdt(const void *blob, int node,
unsigned int cs, unsigned int max_hz, unsigned int mode)
struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node,
int spi_node)
{
struct spi_bus *bus;
unsigned int i;
for (i = 0, bus = spi_bus; i < bus_count; i++, bus++) {
if (bus->node == node)
return spi_setup_slave(i, cs, max_hz, mode);
if (bus->node == spi_node)
return spi_base_setup_slave_fdt(blob, i, slave_node);
}
debug("%s: Failed to find bus node %d\n", __func__, node);
debug("%s: Failed to find bus node %d\n", __func__, spi_node);
return NULL;
}

@ -0,0 +1,204 @@
/*
* Simulate a SPI port
*
* 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 <malloc.h>
#include <spi.h>
#include <os.h>
#include <asm/errno.h>
#include <asm/spi.h>
#include <asm/state.h>
#ifndef CONFIG_SPI_IDLE_VAL
# define CONFIG_SPI_IDLE_VAL 0xFF
#endif
struct sandbox_spi_slave {
struct spi_slave slave;
const struct sandbox_spi_emu_ops *ops;
void *priv;
};
#define to_sandbox_spi_slave(s) container_of(s, struct sandbox_spi_slave, slave)
const char *sandbox_spi_parse_spec(const char *arg, unsigned long *bus,
unsigned long *cs)
{
char *endp;
*bus = simple_strtoul(arg, &endp, 0);
if (*endp != ':' || *bus >= CONFIG_SANDBOX_SPI_MAX_BUS)
return NULL;
*cs = simple_strtoul(endp + 1, &endp, 0);
if (*endp != ':' || *cs >= CONFIG_SANDBOX_SPI_MAX_CS)
return NULL;
return endp + 1;
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return bus < CONFIG_SANDBOX_SPI_MAX_BUS &&
cs < CONFIG_SANDBOX_SPI_MAX_CS;
}
void spi_cs_activate(struct spi_slave *slave)
{
struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave);
debug("sandbox_spi: activating CS\n");
if (sss->ops->cs_activate)
sss->ops->cs_activate(sss->priv);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave);
debug("sandbox_spi: deactivating CS\n");
if (sss->ops->cs_deactivate)
sss->ops->cs_deactivate(sss->priv);
}
void spi_init(void)
{
}
void spi_set_speed(struct spi_slave *slave, uint hz)
{
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct sandbox_spi_slave *sss;
struct sandbox_state *state = state_get_current();
const char *spec;
if (!spi_cs_is_valid(bus, cs)) {
debug("sandbox_spi: Invalid SPI bus/cs\n");
return NULL;
}
sss = spi_alloc_slave(struct sandbox_spi_slave, bus, cs);
if (!sss) {
debug("sandbox_spi: Out of memory\n");
return NULL;
}
spec = state->spi[bus][cs].spec;
sss->ops = state->spi[bus][cs].ops;
if (!spec || !sss->ops || sss->ops->setup(&sss->priv, spec)) {
free(sss);
printf("sandbox_spi: unable to locate a slave client\n");
return NULL;
}
return &sss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave);
debug("sandbox_spi: releasing slave\n");
if (sss->ops->free)
sss->ops->free(sss->priv);
free(sss);
}
static int spi_bus_claim_cnt[CONFIG_SANDBOX_SPI_MAX_BUS];
int spi_claim_bus(struct spi_slave *slave)
{
if (spi_bus_claim_cnt[slave->bus]++) {
printf("sandbox_spi: error: bus already claimed: %d!\n",
spi_bus_claim_cnt[slave->bus]);
}
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
if (--spi_bus_claim_cnt[slave->bus]) {
printf("sandbox_spi: error: bus freed too often: %d!\n",
spi_bus_claim_cnt[slave->bus]);
}
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct sandbox_spi_slave *sss = to_sandbox_spi_slave(slave);
uint bytes = bitlen / 8, i;
int ret = 0;
u8 *tx = (void *)dout, *rx = din;
if (bitlen == 0)
goto done;
/* we can only do 8 bit transfers */
if (bitlen % 8) {
printf("sandbox_spi: xfer: invalid bitlen size %u; needs to be 8bit\n",
bitlen);
flags |= SPI_XFER_END;
goto done;
}
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
/* make sure rx/tx buffers are full so clients can assume */
if (!tx) {
debug("sandbox_spi: xfer: auto-allocating tx scratch buffer\n");
tx = malloc(bytes);
if (!tx) {
debug("sandbox_spi: Out of memory\n");
return -ENOMEM;
}
}
if (!rx) {
debug("sandbox_spi: xfer: auto-allocating rx scratch buffer\n");
rx = malloc(bytes);
if (!rx) {
debug("sandbox_spi: Out of memory\n");
return -ENOMEM;
}
}
debug("sandbox_spi: xfer: bytes = %u\n tx:", bytes);
for (i = 0; i < bytes; ++i)
debug(" %u:%02x", i, tx[i]);
debug("\n");
ret = sss->ops->xfer(sss->priv, tx, rx, bytes);
debug("sandbox_spi: xfer: got back %i (that's %s)\n rx:",
ret, ret ? "bad" : "good");
for (i = 0; i < bytes; ++i)
debug(" %u:%02x", i, rx[i]);
debug("\n");
if (tx != dout)
free(tx);
if (rx != din)
free(rx);
done:
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return ret;
}

@ -5,6 +5,7 @@
*/
#include <common.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
@ -37,3 +38,21 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
return ptr;
}
#ifdef CONFIG_OF_SPI
struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum,
int node)
{
int cs, max_hz, mode = 0;
cs = fdtdec_get_int(blob, node, "reg", -1);
max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 100000);
if (fdtdec_get_bool(blob, node, "spi-cpol"))
mode |= SPI_CPOL;
if (fdtdec_get_bool(blob, node, "spi-cpha"))
mode |= SPI_CPHA;
if (fdtdec_get_bool(blob, node, "spi-cs-high"))
mode |= SPI_CS_HIGH;
return spi_setup_slave(busnum, cs, max_hz, mode);
}
#endif

@ -276,6 +276,7 @@
#define CONFIG_SF_DEFAULT_MODE SPI_MODE_0
#define CONFIG_SF_DEFAULT_SPEED 50000000
#define EXYNOS5_SPI_NUM_CONTROLLERS 5
#define CONFIG_OF_SPI
#endif
#ifdef CONFIG_ENV_IS_IN_SPI_FLASH

@ -71,6 +71,16 @@
#define CONFIG_ENV_SIZE 8192
#define CONFIG_ENV_IS_NOWHERE
/* SPI */
#define CONFIG_SANDBOX_SPI
#define CONFIG_CMD_SF
#define CONFIG_CMD_SF_TEST
#define CONFIG_CMD_SPI
#define CONFIG_SPI_FLASH
#define CONFIG_SPI_FLASH_SANDBOX
#define CONFIG_SPI_FLASH_STMICRO
#define CONFIG_SPI_FLASH_WINBOND
/* Memory things - we don't really want a memory test */
#define CONFIG_SYS_LOAD_ADDR 0x00000000
#define CONFIG_SYS_MEMTEST_START 0x00100000

@ -259,13 +259,24 @@ static inline int spi_w8r8(struct spi_slave *slave, unsigned char byte)
* spi_free_slave() to free it later.
*
* @param blob: Device tree blob
* @param node: SPI peripheral node to use
* @param cs: Chip select to use
* @param max_hz: Maximum SCK rate in Hz (0 for default)
* @param mode: Clock polarity, clock phase and other parameters
* @param slave_node: Slave node to use
* @param spi_node: SPI peripheral node to use
* @return pointer to new spi_slave structure
*/
struct spi_slave *spi_setup_slave_fdt(const void *blob, int node,
unsigned int cs, unsigned int max_hz, unsigned int mode);
struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node,
int spi_node);
/**
* spi_base_setup_slave_fdt() - helper function to set up a SPI slace
*
* This decodes SPI properties from the slave node to determine the
* chip select and SPI parameters.
*
* @blob: Device tree blob
* @busnum: Bus number to use
* @node: Device tree node for the SPI bus
*/
struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum,
int node);
#endif /* _SPI_H_ */

@ -67,6 +67,19 @@ struct spi_flash {
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode);
/**
* Set up a new SPI flash from an fdt node
*
* @param blob Device tree blob
* @param slave_node Pointer to this SPI slave node in the device tree
* @param spi_node Cached pointer to the SPI interface this node belongs
* to
* @return 0 if ok, -1 on error
*/
struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node,
int spi_node);
void spi_flash_free(struct spi_flash *flash);
static inline int spi_flash_read(struct spi_flash *flash, u32 offset,

Loading…
Cancel
Save