diff options
-rw-r--r-- | arch/sandbox/cpu/os.c | 2 | ||||
-rw-r--r-- | arch/sandbox/cpu/start.c | 17 | ||||
-rw-r--r-- | arch/sandbox/include/asm/config.h | 8 | ||||
-rw-r--r-- | arch/sandbox/include/asm/getopt.h | 23 | ||||
-rw-r--r-- | arch/sandbox/include/asm/sections.h | 4 | ||||
-rw-r--r-- | arch/sandbox/include/asm/spi.h | 58 | ||||
-rw-r--r-- | arch/sandbox/include/asm/state.h | 9 | ||||
-rw-r--r-- | board/sandbox/sandbox/README.sandbox | 54 | ||||
-rw-r--r-- | doc/SPI/README.sandbox-spi | 64 | ||||
-rw-r--r-- | doc/device-tree-bindings/spi/spi-bus.txt | 92 | ||||
-rw-r--r-- | drivers/misc/cros_ec_spi.c | 3 | ||||
-rw-r--r-- | drivers/mtd/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/sandbox.c | 483 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_internal.h | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/sf_probe.c | 28 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/exynos_spi.c | 10 | ||||
-rw-r--r-- | drivers/spi/sandbox_spi.c | 204 | ||||
-rw-r--r-- | drivers/spi/spi.c | 19 | ||||
-rw-r--r-- | include/configs/exynos5250-dt.h | 1 | ||||
-rw-r--r-- | include/configs/sandbox.h | 10 | ||||
-rw-r--r-- | include/spi.h | 23 | ||||
-rw-r--r-- | include/spi_flash.h | 13 |
23 files changed, 1088 insertions, 40 deletions
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index db66fd3..26f44cb 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -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; diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index f1cb793..1b15454 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -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[]) { diff --git a/arch/sandbox/include/asm/config.h b/arch/sandbox/include/asm/config.h index 7755a4d..ec7729e 100644 --- a/arch/sandbox/include/asm/config.h +++ b/arch/sandbox/include/asm/config.h @@ -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 diff --git a/arch/sandbox/include/asm/getopt.h b/arch/sandbox/include/asm/getopt.h index 685883c..3048c2c 100644 --- a/arch/sandbox/include/asm/getopt.h +++ b/arch/sandbox/include/asm/getopt.h @@ -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 diff --git a/arch/sandbox/include/asm/sections.h b/arch/sandbox/include/asm/sections.h index 4c37860..fbc1bd1 100644 --- a/arch/sandbox/include/asm/sections.h +++ b/arch/sandbox/include/asm/sections.h @@ -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) diff --git a/arch/sandbox/include/asm/spi.h b/arch/sandbox/include/asm/spi.h new file mode 100644 index 0000000..49b4a0f --- /dev/null +++ b/arch/sandbox/include/asm/spi.h @@ -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 diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 093c81d..a38820b 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -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]; }; /** diff --git a/board/sandbox/sandbox/README.sandbox b/board/sandbox/sandbox/README.sandbox index 30b0541..6989557 100644 --- a/board/sandbox/sandbox/README.sandbox +++ b/board/sandbox/sandbox/README.sandbox @@ -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 ----- diff --git a/doc/SPI/README.sandbox-spi b/doc/SPI/README.sandbox-spi new file mode 100644 index 0000000..bb73eaf --- /dev/null +++ b/doc/SPI/README.sandbox-spi @@ -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>. diff --git a/doc/device-tree-bindings/spi/spi-bus.txt b/doc/device-tree-bindings/spi/spi-bus.txt new file mode 100644 index 0000000..800dafe --- /dev/null +++ b/doc/device-tree-bindings/spi/spi-bus.txt @@ -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>; + }; + }; diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c index 202acf2..2fc9110 100644 --- a/drivers/misc/cros_ec_spi.c +++ b/drivers/misc/cros_ec_spi.c @@ -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; diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 1bbeb7d..26483a2 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -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 diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c new file mode 100644 index 0000000..a62ef4c --- /dev/null +++ b/drivers/mtd/spi/sandbox.c @@ -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>"); diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 732ddf8..d291746 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -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 diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 5eb8ffe..c1eb754 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -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); diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 27902fe..ed4ecd7 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -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 diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 699c57e..4d5def2 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -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; } diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c new file mode 100644 index 0000000..7895305 --- /dev/null +++ b/drivers/spi/sandbox_spi.c @@ -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; +} diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b76a26c..7ddea9b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -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 diff --git a/include/configs/exynos5250-dt.h b/include/configs/exynos5250-dt.h index 0155322..1e9c1e3 100644 --- a/include/configs/exynos5250-dt.h +++ b/include/configs/exynos5250-dt.h @@ -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 diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index a4edc62..7e78a23 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -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 diff --git a/include/spi.h b/include/spi.h index e2563c9..aba7922 100644 --- a/include/spi.h +++ b/include/spi.h @@ -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_ */ diff --git a/include/spi_flash.h b/include/spi_flash.h index 25ca8f1..afc3a58 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.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, |