summaryrefslogtreecommitdiffstats
path: root/op-flasher
diff options
context:
space:
mode:
authorBrad Bishop <bradleyb@fuzziesquirrel.com>2016-05-28 18:41:04 -0400
committerBrad Bishop <bradleyb@fuzziesquirrel.com>2016-06-10 18:06:59 -0400
commit40a360c2a4feef97a8f7041e655b2a42e51e0224 (patch)
tree75dfea3064d7c3243788c72cb9f30e2ce6241dea /op-flasher
parenta73122191a7aba80f97332687a2e03cfb0336981 (diff)
downloadtalos-skeleton-40a360c2a4feef97a8f7041e655b2a42e51e0224.tar.gz
talos-skeleton-40a360c2a4feef97a8f7041e655b2a42e51e0224.zip
Reorganize directory structure
Moving to directory per-application layout. This facilitates building single applications which is useful in the Yocto build environment since different applications satisfy different OpenBMC build requirements. A number of issues are also addressed: - All applications were pulling in libsystemd and the gdbus libs irrespective of whether or not they were needed. - gpio.o duplicated in every application - moved to libopenbmc_intf - Added install target Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
Diffstat (limited to 'op-flasher')
-rw-r--r--op-flasher/Makefile14
-rw-r--r--op-flasher/flasher_obj.c420
-rw-r--r--op-flasher/pflash/Makefile21
-rw-r--r--op-flasher/pflash/TODO7
-rw-r--r--op-flasher/pflash/arm_io.c139
-rw-r--r--op-flasher/pflash/ast-sf-ctrl.c923
-rw-r--r--op-flasher/pflash/ast.h102
-rw-r--r--op-flasher/pflash/ccan/Makefile.inc8
-rw-r--r--op-flasher/pflash/ccan/array_size/LICENSE28
-rw-r--r--op-flasher/pflash/ccan/array_size/_info46
-rw-r--r--op-flasher/pflash/ccan/array_size/array_size.h26
-rw-r--r--op-flasher/pflash/ccan/array_size/test/compile_fail-function-param.c24
-rw-r--r--op-flasher/pflash/ccan/array_size/test/compile_fail.c14
-rw-r--r--op-flasher/pflash/ccan/array_size/test/run.c33
-rw-r--r--op-flasher/pflash/ccan/build_assert/LICENSE28
-rw-r--r--op-flasher/pflash/ccan/build_assert/_info49
-rw-r--r--op-flasher/pflash/ccan/build_assert/build_assert.h40
-rw-r--r--op-flasher/pflash/ccan/build_assert/test/compile_fail-expr.c10
-rw-r--r--op-flasher/pflash/ccan/build_assert/test/compile_fail.c9
-rw-r--r--op-flasher/pflash/ccan/build_assert/test/compile_ok.c7
-rw-r--r--op-flasher/pflash/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c9
-rw-r--r--op-flasher/pflash/ccan/built-in.obin0 -> 14873 bytes
-rw-r--r--op-flasher/pflash/ccan/check_type/LICENSE28
-rw-r--r--op-flasher/pflash/ccan/check_type/_info33
-rw-r--r--op-flasher/pflash/ccan/check_type/check_type.h64
-rw-r--r--op-flasher/pflash/ccan/check_type/test/compile_fail-check_type.c9
-rw-r--r--op-flasher/pflash/ccan/check_type/test/compile_fail-check_type_unsigned.c14
-rw-r--r--op-flasher/pflash/ccan/check_type/test/compile_fail-check_types_match.c10
-rw-r--r--op-flasher/pflash/ccan/check_type/test/run.c22
-rw-r--r--op-flasher/pflash/ccan/container_of/LICENSE28
-rw-r--r--op-flasher/pflash/ccan/container_of/_info63
-rw-r--r--op-flasher/pflash/ccan/container_of/container_of.h109
-rw-r--r--op-flasher/pflash/ccan/container_of/test/compile_fail-bad-type.c22
-rw-r--r--op-flasher/pflash/ccan/container_of/test/compile_fail-types.c22
-rw-r--r--op-flasher/pflash/ccan/container_of/test/compile_fail-var-types.c25
-rw-r--r--op-flasher/pflash/ccan/container_of/test/run.c24
-rw-r--r--op-flasher/pflash/ccan/endian/LICENSE28
-rw-r--r--op-flasher/pflash/ccan/endian/_info53
-rw-r--r--op-flasher/pflash/ccan/endian/endian.h314
-rw-r--r--op-flasher/pflash/ccan/endian/test/compile_ok-constant.c12
-rw-r--r--op-flasher/pflash/ccan/endian/test/run.c106
-rw-r--r--op-flasher/pflash/config.h19
-rw-r--r--op-flasher/pflash/get_arch.sh11
-rw-r--r--op-flasher/pflash/io.h121
-rw-r--r--op-flasher/pflash/libflash/ffs.h144
-rw-r--r--op-flasher/pflash/libflash/libffs.c265
-rw-r--r--op-flasher/pflash/libflash/libffs.h41
-rw-r--r--op-flasher/pflash/libflash/libflash-priv.h217
-rw-r--r--op-flasher/pflash/libflash/libflash.c691
-rw-r--r--op-flasher/pflash/libflash/libflash.h69
-rw-r--r--op-flasher/pflash/libflash/test/Makefile2
-rw-r--r--op-flasher/pflash/libflash/test/test-flash.c403
-rw-r--r--op-flasher/pflash/pflash.c793
-rw-r--r--op-flasher/pflash/powerpc_io.c341
-rw-r--r--op-flasher/pflash/progress.c79
-rw-r--r--op-flasher/pflash/progress.h8
-rw-r--r--op-flasher/pflash/sfc-ctrl.c483
-rw-r--r--op-flasher/pflash/sfc-ctrl.h9
58 files changed, 6639 insertions, 0 deletions
diff --git a/op-flasher/Makefile b/op-flasher/Makefile
new file mode 100644
index 0000000..80b9ccb
--- /dev/null
+++ b/op-flasher/Makefile
@@ -0,0 +1,14 @@
+BINS=flasher
+EXTRA_OBJS+=progress.o \
+ ast-sf-ctrl.o \
+ libflash.o \
+ libffs.o \
+ arm_io.o
+CFLAGS+=-Ipflash
+include ../gdbus.mk
+include ../rules.mk
+
+%.o: pflash/%.c
+ $(CC) -c -o $(CFLAGS) -fPIC -o $@ $<
+%.o: pflash/libflash/%.c
+ $(CC) -c -o $(CFLAGS) -fPIC -o $@ $<
diff --git a/op-flasher/flasher_obj.c b/op-flasher/flasher_obj.c
new file mode 100644
index 0000000..95892bd
--- /dev/null
+++ b/op-flasher/flasher_obj.c
@@ -0,0 +1,420 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include "progress.h"
+#include "io.h"
+#include "ast.h"
+#include "sfc-ctrl.h"
+#include "interfaces/openbmc_intf.h"
+#include "openbmc.h"
+
+static const gchar* dbus_object_path = "/org/openbmc/control";
+static const gchar* dbus_name = "org.openbmc.control.Flasher";
+
+static GDBusObjectManagerServer *manager = NULL;
+
+#define __aligned(x) __attribute__((aligned(x)))
+
+#define PFLASH_VERSION "0.8.6"
+
+static bool need_relock;
+#ifdef __powerpc__
+static bool using_sfc;
+#endif
+
+#define FILE_BUF_SIZE 0x10000
+static uint8_t file_buf[FILE_BUF_SIZE] __aligned(0x1000);
+
+static struct spi_flash_ctrl *fl_ctrl;
+static struct flash_chip *fl_chip;
+static struct ffs_handle *ffsh;
+static uint32_t fl_total_size, fl_erase_granule;
+static const char *fl_name;
+static int32_t ffs_index = -1;
+
+static uint8_t FLASH_OK = 0;
+static uint8_t FLASH_ERROR = 0x01;
+static uint8_t FLASH_SETUP_ERROR = 0x02;
+
+static int
+erase_chip(void)
+{
+ int rc = 0;
+
+ printf("Erasing... (may take a while !) ");
+ fflush(stdout);
+
+ rc = flash_erase_chip(fl_chip);
+ if(rc) {
+ fprintf(stderr, "Error %d erasing chip\n", rc);
+ return(rc);
+ }
+
+ printf("done !\n");
+ return(rc);
+}
+
+void
+flash_message(GDBusConnection* connection,char* obj_path,char* method, char* error_msg)
+{
+ GDBusProxy *proxy;
+ GError *error;
+ GVariant *parm = NULL;
+ error = NULL;
+ proxy = g_dbus_proxy_new_sync(connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, /* GDBusInterfaceInfo* */
+ "org.openbmc.control.Flash", /* name */
+ obj_path, /* object path */
+ "org.openbmc.Flash", /* interface name */
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error(error);
+
+ error = NULL;
+ if(strcmp(method,"error")==0) {
+ parm = g_variant_new("(s)",error_msg);
+ }
+ g_dbus_proxy_call_sync(proxy,
+ method,
+ parm,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ g_assert_no_error(error);
+}
+
+static int
+program_file(FlashControl* flash_control, const char *file, uint32_t start, uint32_t size)
+{
+ int fd, rc;
+ ssize_t len;
+ uint32_t actual_size = 0;
+
+ fd = open(file, O_RDONLY);
+ if(fd == -1) {
+ perror("Failed to open file");
+ return(fd);
+ }
+ printf("About to program \"%s\" at 0x%08x..0x%08x !\n",
+ file, start, size);
+
+ printf("Programming & Verifying...\n");
+ //progress_init(size >> 8);
+ unsigned int save_size = size;
+ uint8_t last_progress = 0;
+ while(size) {
+ len = read(fd, file_buf, FILE_BUF_SIZE);
+ if(len < 0) {
+ perror("Error reading file");
+ return(1);
+ }
+ if(len == 0)
+ break;
+ if(len > size)
+ len = size;
+ size -= len;
+ actual_size += len;
+ rc = flash_write(fl_chip, start, file_buf, len, true);
+ if(rc) {
+ if(rc == FLASH_ERR_VERIFY_FAILURE)
+ fprintf(stderr, "Verification failed for"
+ " chunk at 0x%08x\n", start);
+ else
+ fprintf(stderr, "Flash write error %d for"
+ " chunk at 0x%08x\n", rc, start);
+ return(rc);
+ }
+ start += len;
+ unsigned int percent = (100*actual_size/save_size);
+ uint8_t progress = (uint8_t)(percent);
+ if(progress != last_progress) {
+ flash_control_emit_progress(flash_control,file,progress);
+ last_progress = progress;
+ }
+ }
+ close(fd);
+
+ /* If this is a flash partition, adjust its size */
+ if(ffsh && ffs_index >= 0) {
+ printf("Updating actual size in partition header...\n");
+ ffs_update_act_size(ffsh, ffs_index, actual_size);
+ }
+ return(0);
+}
+
+static void
+flash_access_cleanup_bmc(void)
+{
+ if(ffsh)
+ ffs_close(ffsh);
+ flash_exit(fl_chip);
+ ast_sf_close(fl_ctrl);
+ close_devs();
+}
+
+static int
+flash_access_setup_bmc(bool use_lpc, bool need_write)
+{
+ int rc;
+ printf("Setting up BMC flash\n");
+ /* Open and map devices */
+ open_devs(use_lpc, true);
+
+ /* Create the AST flash controller */
+ rc = ast_sf_open(AST_SF_TYPE_BMC, &fl_ctrl);
+ if(rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ return FLASH_SETUP_ERROR;
+ }
+
+ /* Open flash chip */
+ rc = flash_init(fl_ctrl, &fl_chip);
+ if(rc) {
+ fprintf(stderr, "Failed to open flash chip\n");
+ return FLASH_SETUP_ERROR;
+ }
+
+ /* Setup cleanup function */
+ atexit(flash_access_cleanup_bmc);
+ return FLASH_OK;
+}
+
+static void
+flash_access_cleanup_pnor(void)
+{
+ /* Re-lock flash */
+ if(need_relock)
+ set_wrprotect(true);
+
+ if(ffsh)
+ ffs_close(ffsh);
+ flash_exit(fl_chip);
+#ifdef __powerpc__
+ if(using_sfc)
+ sfc_close(fl_ctrl);
+ else
+ ast_sf_close(fl_ctrl);
+#else
+ ast_sf_close(fl_ctrl);
+#endif
+ close_devs();
+}
+
+static int
+flash_access_setup_pnor(bool use_lpc, bool use_sfc, bool need_write)
+{
+ int rc;
+ printf("Setting up BIOS flash\n");
+
+ /* Open and map devices */
+ open_devs(use_lpc, false);
+
+#ifdef __powerpc__
+ if(use_sfc) {
+ /* Create the SFC flash controller */
+ rc = sfc_open(&fl_ctrl);
+ if(rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ return FLASH_SETUP_ERROR;
+ }
+ using_sfc = true;
+ } else {
+#endif
+ /* Create the AST flash controller */
+ rc = ast_sf_open(AST_SF_TYPE_PNOR, &fl_ctrl);
+ if(rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ return FLASH_SETUP_ERROR;
+ }
+#ifdef __powerpc__
+ }
+#endif
+
+ /* Open flash chip */
+ rc = flash_init(fl_ctrl, &fl_chip);
+ if(rc) {
+ fprintf(stderr, "Failed to open flash chip\n");
+ return FLASH_SETUP_ERROR;
+ }
+
+ /* Unlock flash (PNOR only) */
+ if(need_write)
+ need_relock = set_wrprotect(false);
+
+ /* Setup cleanup function */
+ atexit(flash_access_cleanup_pnor);
+ return FLASH_OK;
+}
+
+uint8_t
+flash(FlashControl* flash_control,bool bmc_flash, uint32_t address, char* write_file, char* obj_path)
+{
+ bool has_sfc = false, has_ast = false, use_lpc = true;
+ bool erase = true, program = true;
+
+ int rc;
+ printf("flasher: %s, BMC = %d, address = 0x%x\n",write_file,bmc_flash,address);
+#ifdef __arm__
+ /* Check platform */
+ check_platform(&has_sfc, &has_ast);
+
+ /* Prepare for access */
+ if(bmc_flash) {
+ if(!has_ast) {
+ fprintf(stderr, "No BMC on this platform\n");
+ return FLASH_SETUP_ERROR;
+ }
+ rc = flash_access_setup_bmc(use_lpc, erase || program);
+ if(rc) {
+ return FLASH_SETUP_ERROR;
+ }
+ } else {
+ if(!has_ast && !has_sfc) {
+ fprintf(stderr, "No BMC nor SFC on this platform\n");
+ return FLASH_SETUP_ERROR;
+ }
+ rc = flash_access_setup_pnor(use_lpc, has_sfc, erase || program);
+ if(rc) {
+ return FLASH_SETUP_ERROR;
+ }
+ }
+
+ rc = flash_get_info(fl_chip, &fl_name,
+ &fl_total_size, &fl_erase_granule);
+ if(rc) {
+ fprintf(stderr, "Error %d getting flash info\n", rc);
+ return FLASH_SETUP_ERROR;
+ }
+#endif
+ if(strcmp(write_file,"")!=0)
+ {
+ // If file specified but not size, get size from file
+ struct stat stbuf;
+ if(stat(write_file, &stbuf)) {
+ perror("Failed to get file size");
+ return FLASH_ERROR;
+ }
+ uint32_t write_size = stbuf.st_size;
+#ifdef __arm__
+ rc = erase_chip();
+ if(rc) {
+ return FLASH_ERROR;
+ }
+ rc = program_file(flash_control, write_file, address, write_size);
+ if(rc) {
+ return FLASH_ERROR;
+ }
+#endif
+
+ printf("Flash done\n");
+ }
+ else
+ {
+ printf("Flash tuned\n");
+ }
+ return FLASH_OK;
+}
+
+static void
+on_bus_acquired(GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ cmdline *cmd = user_data;
+ if(cmd->argc < 4)
+ {
+ g_print("flasher [flash name] [filename] [source object]\n");
+ g_main_loop_quit(cmd->loop);
+ return;
+ }
+ printf("Starting flasher: %s,%s,%s,\n",cmd->argv[1],cmd->argv[2],cmd->argv[3]);
+ ObjectSkeleton *object;
+ manager = g_dbus_object_manager_server_new(dbus_object_path);
+ gchar *s;
+ s = g_strdup_printf("%s/%s",dbus_object_path,cmd->argv[1]);
+
+ object = object_skeleton_new(s);
+ g_free(s);
+
+ FlashControl* flash_control = flash_control_skeleton_new();
+ object_skeleton_set_flash_control(object, flash_control);
+ g_object_unref(flash_control);
+
+ /* Export the object (@manager takes its own reference to @object) */
+ g_dbus_object_manager_server_export(manager, G_DBUS_OBJECT_SKELETON(object));
+ g_object_unref(object);
+
+ /* Export all objects */
+ g_dbus_object_manager_server_set_connection(manager, connection);
+ bool bmc_flash = false;
+ uint32_t address = 0;
+ if(strcmp(cmd->argv[1],"bmc")==0) {
+ bmc_flash = true;
+ }
+ if(strcmp(cmd->argv[1],"bmc_ramdisk")==0) {
+ bmc_flash = true;
+ address = 0x20300000;
+ }
+ if(strcmp(cmd->argv[1],"bmc_kernel")==0) {
+ bmc_flash = true;
+ address = 0x20080000;
+ }
+
+ int rc = flash(flash_control,bmc_flash,address,cmd->argv[2],cmd->argv[3]);
+ if(rc) {
+ flash_message(connection,cmd->argv[3],"error","Flash Error");
+ } else {
+ flash_message(connection,cmd->argv[3],"done","");
+ }
+
+ //Object exits when done flashing
+ g_main_loop_quit(cmd->loop);
+}
+
+int
+main(int argc, char *argv[])
+{
+ GMainLoop *loop;
+ cmdline cmd;
+ cmd.argc = argc;
+ cmd.argv = argv;
+
+ guint id;
+ loop = g_main_loop_new(NULL, FALSE);
+ cmd.loop = loop;
+
+ id = g_bus_own_name(DBUS_TYPE,
+ dbus_name,
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+ G_BUS_NAME_OWNER_FLAGS_REPLACE,
+ on_bus_acquired,
+ NULL,
+ NULL,
+ &cmd,
+ NULL);
+
+ g_main_loop_run(loop);
+
+ g_bus_unown_name(id);
+ g_main_loop_unref(loop);
+
+ return 0;
+}
diff --git a/op-flasher/pflash/Makefile b/op-flasher/pflash/Makefile
new file mode 100644
index 0000000..f77cee9
--- /dev/null
+++ b/op-flasher/pflash/Makefile
@@ -0,0 +1,21 @@
+ ARCH_OBJS = arm_io.o
+
+CFLAGS = -O2 -Wall -I.
+LDFLAGS = -lrt
+OBJS = pflash.o progress.o ast-sf-ctrl.o
+OBJS += libflash/libflash.o libflash/libffs.o
+OBJS += $(ARCH_OBJS)
+EXE = pflash
+
+#CC = $(CROSS_COMPILE)gcc
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c $< -o $@ -I.
+
+$(EXE): $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -I.
+
+clean:
+ rm -f $(OBJS) $(EXE) *.o *.d libflash/test/test_flash libflash/test/*.o
+distclean: clean
+ rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~
diff --git a/op-flasher/pflash/TODO b/op-flasher/pflash/TODO
new file mode 100644
index 0000000..b30f36d
--- /dev/null
+++ b/op-flasher/pflash/TODO
@@ -0,0 +1,7 @@
+- PCI backend for host
+- Use proper GPIO APIs on ARM
+- Use IPMI for lock/unlock on host
+- Timeouts and flashing errors handling
+- Lock handling
+- Support pnor "update" mode which only update selected partitions
+
diff --git a/op-flasher/pflash/arm_io.c b/op-flasher/pflash/arm_io.c
new file mode 100644
index 0000000..d97bd10
--- /dev/null
+++ b/op-flasher/pflash/arm_io.c
@@ -0,0 +1,139 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include "io.h"
+
+void *ahb_reg_map;
+void *ahb_flash_map;
+uint32_t ahb_flash_base, ahb_flash_size;
+void *gpio_ctrl;
+
+int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len)
+{
+ if (reg < ahb_flash_base ||
+ (reg + len) > (ahb_flash_base + ahb_flash_size))
+ return -1;
+ reg -= ahb_flash_base;
+
+ if (((reg | (unsigned long)src | len) & 3) == 0) {
+ while(len > 3) {
+ uint32_t val = *(uint32_t *)src;
+ writel(val, ahb_flash_map + reg);
+ src += 4;
+ reg += 4;
+ len -= 4;
+ }
+ }
+
+ while(len--) {
+ uint8_t val = *(uint8_t *)src;
+ writeb(val, ahb_flash_map + reg++);
+ src += 1;
+ }
+ return 0;
+}
+
+
+int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len)
+{
+ if (reg < ahb_flash_base ||
+ (reg + len) > (ahb_flash_base + ahb_flash_size))
+ return -1;
+ reg -= ahb_flash_base;
+
+ if (((reg | (unsigned long)dst | len) & 3) == 0) {
+ while(len > 3) {
+ *(uint32_t *)dst = readl(ahb_flash_map + reg);
+ dst += 4;
+ reg += 4;
+ len -= 4;
+ }
+ }
+
+ while(len--) {
+ *(uint8_t *)dst = readb(ahb_flash_map + reg++);
+ dst += 1;
+ }
+ return 0;
+}
+
+/*
+ * GPIO stuff to be replaced by higher level accessors for
+ * controlling the flash write lock via sysfs
+ */
+
+static inline uint32_t gpio_ctl_readl(uint32_t offset)
+{
+ return readl(gpio_ctrl + offset);
+}
+
+static inline void gpio_ctl_writel(uint32_t val, uint32_t offset)
+{
+ writel(val, gpio_ctrl + offset);
+}
+
+
+bool set_wrprotect(bool protect)
+{
+ uint32_t reg;
+ bool was_protected;
+
+ reg = gpio_ctl_readl(0x20);
+ was_protected = !!(reg & 0x00004000);
+ if (protect)
+ reg |= 0x00004000; /* GPIOF[6] value */
+ else
+ reg &= ~0x00004000; /* GPIOF[6] value */
+ gpio_ctl_writel(reg, 0x20);
+ reg = gpio_ctl_readl(0x24);
+ reg |= 0x00004000; /* GPIOF[6] direction */
+ gpio_ctl_writel(reg, 0x24);
+
+ return was_protected;
+}
+
+void open_devs(bool use_lpc, bool bmc_flash)
+{
+ int fd;
+
+ (void)use_lpc;
+
+ fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if (fd < 0) {
+ perror("can't open /dev/mem");
+ exit(1);
+ }
+ ahb_reg_map = mmap(0, AHB_REGS_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, AHB_REGS_BASE);
+ if (ahb_reg_map == MAP_FAILED) {
+ perror("can't map AHB registers /dev/mem");
+ exit(1);
+ }
+ gpio_ctrl = mmap(0, GPIO_CTRL_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, GPIO_CTRL_BASE);
+ if (gpio_ctrl == MAP_FAILED) {
+ perror("can't map GPIO control via /dev/mem");
+ exit(1);
+ }
+ ahb_flash_base = bmc_flash ? BMC_FLASH_BASE : PNOR_FLASH_BASE;
+ ahb_flash_size = bmc_flash ? BMC_FLASH_SIZE : PNOR_FLASH_SIZE;
+ ahb_flash_map = mmap(0, ahb_flash_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, ahb_flash_base);
+ if (ahb_flash_map == MAP_FAILED) {
+ perror("can't map flash via /dev/mem");
+ exit(1);
+ }
+}
diff --git a/op-flasher/pflash/ast-sf-ctrl.c b/op-flasher/pflash/ast-sf-ctrl.c
new file mode 100644
index 0000000..bf5d372
--- /dev/null
+++ b/op-flasher/pflash/ast-sf-ctrl.c
@@ -0,0 +1,923 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "ast.h"
+
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+
+#define CALIBRATE_BUF_SIZE 16384
+
+struct ast_sf_ctrl {
+ /* We have 2 controllers, one for the BMC flash, one for the PNOR */
+ uint8_t type;
+
+ /* Address and previous value of the ctrl register */
+ uint32_t ctl_reg;
+
+ /* Control register value for normal commands */
+ uint32_t ctl_val;
+
+ /* Control register value for (fast) reads */
+ uint32_t ctl_read_val;
+
+ /* Flash read timing register */
+ uint32_t fread_timing_reg;
+ uint32_t fread_timing_val;
+
+ /* Address of the flash mapping */
+ uint32_t flash;
+
+ /* Current 4b mode */
+ bool mode_4b;
+
+ /* Callbacks */
+ struct spi_flash_ctrl ops;
+};
+
+static uint32_t ast_ahb_freq;
+
+static const uint32_t ast_ct_hclk_divs[] = {
+ 0xf, /* HCLK */
+ 0x7, /* HCLK/2 */
+ 0xe, /* HCLK/3 */
+ 0x6, /* HCLK/4 */
+ 0xd, /* HCLK/5 */
+};
+
+static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd)
+{
+ /* Switch to user mode, CE# dropped */
+ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
+
+ /* user mode, CE# active */
+ ast_ahb_writel(ct->ctl_val | 3, ct->ctl_reg);
+
+ /* write cmd */
+ return ast_copy_to_ahb(ct->flash, &cmd, 1);
+}
+
+static void ast_sf_end_cmd(struct ast_sf_ctrl *ct)
+{
+ /* clear CE# */
+ ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);
+
+ /* Switch back to read mode */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+}
+
+static int ast_sf_send_addr(struct ast_sf_ctrl *ct, uint32_t addr)
+{
+ const void *ap;
+
+ /* Layout address MSB first in memory */
+ addr = cpu_to_be32(addr);
+
+ /* Send the right amount of bytes */
+ ap = (char *)&addr;
+
+ if (ct->mode_4b)
+ return ast_copy_to_ahb(ct->flash, ap, 4);
+ else
+ return ast_copy_to_ahb(ct->flash, ap + 1, 3);
+}
+
+static int ast_sf_cmd_rd(struct spi_flash_ctrl *ctrl, uint8_t cmd,
+ bool has_addr, uint32_t addr, void *buffer,
+ uint32_t size)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ int rc;
+
+ rc = ast_sf_start_cmd(ct, cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = ast_sf_send_addr(ct, addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = ast_copy_from_ahb(buffer, ct->flash, size);
+ bail:
+ ast_sf_end_cmd(ct);
+ return rc;
+}
+
+static int ast_sf_cmd_wr(struct spi_flash_ctrl *ctrl, uint8_t cmd,
+ bool has_addr, uint32_t addr, const void *buffer,
+ uint32_t size)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ int rc;
+
+ rc = ast_sf_start_cmd(ct, cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = ast_sf_send_addr(ct, addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = ast_copy_to_ahb(ct->flash, buffer, size);
+ bail:
+ ast_sf_end_cmd(ct);
+ return rc;
+}
+
+static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ uint32_t ce_ctrl = 0;
+
+ if (ct->type == AST_SF_TYPE_BMC && ct->ops.finfo->size > 0x1000000)
+ ce_ctrl = ast_ahb_readl(BMC_SPI_FCTL_CE_CTRL);
+
+ /*
+ * We update the "old" value as well since when quitting
+ * we don't restore the mode of the flash itself so we need
+ * to leave the controller in a compatible setup
+ */
+ if (enable) {
+ ct->ctl_val |= 0x2000;
+ ct->ctl_read_val |= 0x2000;
+ ce_ctrl |= 0x1;
+ } else {
+ ct->ctl_val &= ~0x2000;
+ ct->ctl_read_val &= ~0x2000;
+ ce_ctrl &= ~0x1;
+ }
+ ct->mode_4b = enable;
+
+ /* Update read mode */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ if (ce_ctrl)
+ ast_ahb_writel(ce_ctrl, BMC_SPI_FCTL_CE_CTRL);
+
+ return 0;
+}
+
+static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+
+ /*
+ * We are in read mode by default. We don't yet support fancy
+ * things like fast read or X2 mode
+ */
+ return ast_copy_from_ahb(buf, ct->flash + pos, len);
+}
+
+static void ast_get_ahb_freq(void)
+{
+ static const uint32_t cpu_freqs_24_48[] = {
+ 384000000,
+ 360000000,
+ 336000000,
+ 408000000
+ };
+ static const uint32_t cpu_freqs_25[] = {
+ 400000000,
+ 375000000,
+ 350000000,
+ 425000000
+ };
+ static const uint32_t ahb_div[] = { 1, 2, 4, 3 };
+ uint32_t strap, cpu_clk, div;
+
+ if (ast_ahb_freq)
+ return;
+
+ /* HW strapping gives us the CPU freq and AHB divisor */
+ strap = ast_ahb_readl(SCU_HW_STRAPPING);
+ if (strap & 0x00800000) {
+ FL_DBG("AST: CLKIN 25Mhz\n");
+ cpu_clk = cpu_freqs_25[(strap >> 8) & 3];
+ } else {
+ FL_DBG("AST: CLKIN 24/48Mhz\n");
+ cpu_clk = cpu_freqs_24_48[(strap >> 8) & 3];
+ }
+ FL_DBG("AST: CPU frequency: %d Mhz\n", cpu_clk / 1000000);
+ div = ahb_div[(strap >> 10) & 3];
+ ast_ahb_freq = cpu_clk / div;
+ FL_DBG("AST: AHB frequency: %d Mhz\n", ast_ahb_freq / 1000000);
+}
+
+static int ast_sf_check_reads(struct ast_sf_ctrl *ct,
+ const uint8_t *golden_buf, uint8_t *test_buf)
+{
+ int i, rc;
+
+ for (i = 0; i < 10; i++) {
+ rc = ast_copy_from_ahb(test_buf, ct->flash, CALIBRATE_BUF_SIZE);
+ if (rc)
+ return rc;
+ if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0)
+ return FLASH_ERR_VERIFY_FAILURE;
+ }
+ return 0;
+}
+
+static int ast_sf_calibrate_reads(struct ast_sf_ctrl *ct, uint32_t hdiv,
+ const uint8_t *golden_buf, uint8_t *test_buf)
+{
+ int i, rc;
+ int good_pass = -1, pass_count = 0;
+ uint32_t shift = (hdiv - 1) << 2;
+ uint32_t mask = ~(0xfu << shift);
+
+#define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8))
+
+ /* Try HCLK delay 0..5, each one with/without delay and look for a
+ * good pair.
+ */
+ for (i = 0; i < 12; i++) {
+ bool pass;
+
+ ct->fread_timing_val &= mask;
+ ct->fread_timing_val |= FREAD_TPASS(i) << shift;
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
+ rc = ast_sf_check_reads(ct, golden_buf, test_buf);
+ if (rc && rc != FLASH_ERR_VERIFY_FAILURE)
+ return rc;
+ pass = (rc == 0);
+ FL_DBG(" * [%08x] %d HCLK delay, %dns DI delay : %s\n",
+ ct->fread_timing_val, i/2, (i & 1) ? 0 : 4, pass ? "PASS" : "FAIL");
+ if (pass) {
+ pass_count++;
+ if (pass_count == 3) {
+ good_pass = i - 1;
+ break;
+ }
+ } else
+ pass_count = 0;
+ }
+
+ /* No good setting for this frequency */
+ if (good_pass < 0)
+ return FLASH_ERR_VERIFY_FAILURE;
+
+ /* We have at least one pass of margin, let's use first pass */
+ ct->fread_timing_val &= mask;
+ ct->fread_timing_val |= FREAD_TPASS(good_pass) << shift;
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
+ FL_DBG("AST: * -> good is pass %d [0x%08x]\n",
+ good_pass, ct->fread_timing_val);
+ return 0;
+}
+
+static bool ast_calib_data_usable(const uint8_t *test_buf, uint32_t size)
+{
+ const uint32_t *tb32 = (const uint32_t *)test_buf;
+ uint32_t i, cnt = 0;
+
+ /* We check if we have enough words that are neither all 0
+ * nor all 1's so the calibration can be considered valid.
+ *
+ * I use an arbitrary threshold for now of 64
+ */
+ size >>= 2;
+ for (i = 0; i < size; i++) {
+ if (tb32[i] != 0 && tb32[i] != 0xffffffff)
+ cnt++;
+ }
+ return cnt >= 64;
+}
+
+static int ast_sf_optimize_reads(struct ast_sf_ctrl *ct,
+ struct flash_info *info __unused,
+ uint32_t max_freq)
+{
+ uint8_t *golden_buf, *test_buf;
+ int i, rc, best_div = -1;
+ uint32_t save_read_val = ct->ctl_read_val;
+
+ test_buf = malloc(CALIBRATE_BUF_SIZE * 2);
+ golden_buf = test_buf + CALIBRATE_BUF_SIZE;
+
+ /* We start with the dumbest setting and read some data */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# max */
+ (0x03 << 16) | /* use normal reads */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ rc = ast_copy_from_ahb(golden_buf, ct->flash, CALIBRATE_BUF_SIZE);
+ if (rc) {
+ free(test_buf);
+ return rc;
+ }
+
+ /* Establish our read mode with freq field set to 0 */
+ ct->ctl_read_val = save_read_val & 0xfffff0ff;
+
+ /* Check if calibration data is suitable */
+ if (!ast_calib_data_usable(golden_buf, CALIBRATE_BUF_SIZE)) {
+ FL_INF("AST: Calibration area too uniform, "
+ "using low speed\n");
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ free(test_buf);
+ return 0;
+ }
+
+ /* Now we iterate the HCLK dividers until we find our breaking point */
+ for (i = 5; i > 0; i--) {
+ uint32_t tv, freq;
+
+ /* Compare timing to max */
+ freq = ast_ahb_freq / i;
+ if (freq >= max_freq)
+ continue;
+
+ /* Set the timing */
+ tv = ct->ctl_read_val | (ast_ct_hclk_divs[i - 1] << 8);
+ ast_ahb_writel(tv, ct->ctl_reg);
+ FL_DBG("AST: Trying HCLK/%d...\n", i);
+ rc = ast_sf_calibrate_reads(ct, i, golden_buf, test_buf);
+
+ /* Some other error occurred, bail out */
+ if (rc && rc != FLASH_ERR_VERIFY_FAILURE) {
+ free(test_buf);
+ return rc;
+ }
+ if (rc == 0)
+ best_div = i;
+ }
+ free(test_buf);
+
+ /* Nothing found ? */
+ if (best_div < 0)
+ FL_ERR("AST: No good frequency, using dumb slow\n");
+ else {
+ FL_DBG("AST: Found good read timings at HCLK/%d\n", best_div);
+ ct->ctl_read_val |= (ast_ct_hclk_divs[best_div - 1] << 8);
+ }
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ return 0;
+}
+
+static int ast_sf_get_hclk(uint32_t *ctl_val, uint32_t max_freq)
+{
+ int i;
+
+ /* It appears that running commands at HCLK/2 on some micron
+ * chips results in occasionally reads of bogus status (that
+ * or unrelated chip hangs).
+ *
+ * Since we cannot calibrate properly the reads for commands,
+ * instead, let's limit our SPI frequency to HCLK/4 to stay
+ * on the safe side of things
+ */
+#define MIN_CMD_FREQ 4
+ for (i = MIN_CMD_FREQ; i <= 5; i++) {
+ uint32_t freq = ast_ahb_freq / i;
+ if (freq >= max_freq)
+ continue;
+ *ctl_val |= (ast_ct_hclk_divs[i - 1] << 8);
+ return i;
+ }
+ return 0;
+}
+
+static int ast_sf_setup_macronix(struct ast_sf_ctrl *ct, struct flash_info *info)
+{
+ int rc, div __unused;
+ uint8_t srcr[2];
+
+ /*
+ * Those Macronix chips support dual reads at 104Mhz
+ * and dual IO at 84Mhz with 4 dummies.
+ *
+ * Our calibration algo should give us something along
+ * the lines of HCLK/3 (HCLK/2 seems to work sometimes
+ * but appears to be fairly unreliable) which is 64Mhz
+ *
+ * So we chose dual IO mode.
+ *
+ * The CE# inactive width for reads must be 7ns, we set it
+ * to 3T which is about 15ns at the fastest speed we support
+ * HCLK/2) as I've had issue with smaller values.
+ *
+ * For write and program it's 30ns so let's set the value
+ * for normal ops to 6T.
+ *
+ * Preserve the current 4b mode.
+ */
+ FL_DBG("AST: Setting up Macronix...\n");
+
+ /*
+ * Read the status and config registers
+ */
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDSR, false, 0, &srcr[0], 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read status\n");
+ return rc;
+ }
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDCR, false, 0, &srcr[1], 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read configuration\n");
+ return rc;
+ }
+
+ FL_DBG("AST: Macronix SR:CR: 0x%02x:%02x\n", srcr[0], srcr[1]);
+
+ /* Switch to 8 dummy cycles to enable 104Mhz operations */
+ srcr[1] = (srcr[1] & 0x3f) | 0x80;
+
+ rc = fl_wren(&ct->ops);
+ if (rc) {
+ FL_ERR("AST: Failed to WREN for Macronix config\n");
+ return rc;
+ }
+
+ rc = ast_sf_cmd_wr(&ct->ops, CMD_WRSR, false, 0, srcr, 2);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to write Macronix config\n");
+ return rc;
+ }
+ rc = fl_sync_wait_idle(&ct->ops);;
+ if (rc != 0) {
+ FL_ERR("AST: Failed waiting for config write\n");
+ return rc;
+ }
+
+ FL_DBG("AST: Macronix SR:CR: 0x%02x:%02x\n", srcr[0], srcr[1]);
+
+ /* Use 2READ */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x03 << 28) | /* Dual IO */
+ (0x0d << 24) | /* CE# width 3T */
+ (0xbb << 16) | /* 2READ command */
+ (0x00 << 8) | /* HCLK/16 (optimize later) */
+ (0x02 << 6) | /* 2 bytes dummy cycle (8 clocks) */
+ (0x01); /* fast read */
+
+ /* Configure SPI flash read timing */
+ rc = ast_sf_optimize_reads(ct, info, 104000000);
+ if (rc) {
+ FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * For other commands and writes also increase the SPI clock
+ * to HCLK/2 since the chip supports up to 133Mhz and set
+ * CE# inactive to 6T. We request a timing that is 20% below
+ * the limit of the chip, so about 106Mhz which should fit.
+ */
+ ct->ctl_val = (ct->ctl_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x0a << 24) | /* CE# width 6T (b1010) */
+ (0x00 << 16) | /* no command */
+ (0x00 << 8) | /* HCLK/16 (done later) */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ div = ast_sf_get_hclk(&ct->ctl_val, 106000000);
+ FL_DBG("AST: Command timing set to HCLK/%d\n", div);
+
+ /* Update chip with current read config */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ return 0;
+}
+
+static int ast_sf_setup_winbond(struct ast_sf_ctrl *ct, struct flash_info *info)
+{
+ int rc, div __unused;
+
+ FL_DBG("AST: Setting up Windbond...\n");
+
+ /*
+ * This Windbond chip support dual reads at 104Mhz
+ * with 8 dummy cycles.
+ *
+ * The CE# inactive width for reads must be 10ns, we set it
+ * to 3T which is about 15.6ns.
+ */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x02 << 28) | /* Dual bit data only */
+ (0x0e << 24) | /* CE# width 2T (b1110) */
+ (0x3b << 16) | /* DREAD command */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x01 << 6) | /* 1-byte dummy cycle */
+ (0x01); /* fast read */
+
+ /* Configure SPI flash read timing */
+ rc = ast_sf_optimize_reads(ct, info, 104000000);
+ if (rc) {
+ FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * For other commands and writes also increase the SPI clock
+ * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive
+ * for write and erase is 50ns so let's set it to 10T.
+ */
+ ct->ctl_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x06 << 24) | /* CE# width 10T (b0110) */
+ (0x00 << 16) | /* no command */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x01); /* fast read */
+
+ div = ast_sf_get_hclk(&ct->ctl_val, 106000000);
+ FL_DBG("AST: Command timing set to HCLK/%d\n", div);
+
+ /* Update chip with current read config */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ return 0;
+}
+
+static int ast_sf_setup_micron(struct ast_sf_ctrl *ct, struct flash_info *info)
+{
+ uint8_t vconf, ext_id[6];
+ int rc, div __unused;
+
+ FL_DBG("AST: Setting up Micron...\n");
+
+ /*
+ * Read the extended chip ID to try to detect old vs. new
+ * flashes since old Micron flashes have a lot of issues
+ */
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_RDID, false, 0, ext_id, 6);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read Micron ext ID, sticking to dumb speed\n");
+ return 0;
+ }
+ /* Check ID matches expectations */
+ if (ext_id[0] != ((info->id >> 16) & 0xff) ||
+ ext_id[1] != ((info->id >> 8) & 0xff) ||
+ ext_id[2] != ((info->id ) & 0xff)) {
+ FL_ERR("AST: Micron ext ID mismatch, sticking to dumb speed\n");
+ return 0;
+ }
+ FL_DBG("AST: Micron ext ID byte: 0x%02x\n", ext_id[4]);
+
+ /* Check for old (<45nm) chips, don't try to be fancy on those */
+ if (!(ext_id[4] & 0x40)) {
+ FL_DBG("AST: Old chip, using dumb timings\n");
+ goto dumb;
+ }
+
+ /*
+ * Read the micron specific volatile configuration reg
+ */
+ rc = ast_sf_cmd_rd(&ct->ops, CMD_MIC_RDVCONF, false, 0, &vconf, 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to read Micron vconf, sticking to dumb speed\n");
+ goto dumb;
+ }
+ FL_DBG("AST: Micron VCONF: 0x%02x\n", vconf);
+
+ /* Switch to 8 dummy cycles (we might be able to operate with 4
+ * but let's keep some margin
+ */
+ vconf = (vconf & 0x0f) | 0x80;
+
+ rc = ast_sf_cmd_wr(&ct->ops, CMD_MIC_WRVCONF, false, 0, &vconf, 1);
+ if (rc != 0) {
+ FL_ERR("AST: Failed to write Micron vconf, "
+ " sticking to dumb speed\n");
+ goto dumb;
+ }
+ rc = fl_sync_wait_idle(&ct->ops);;
+ if (rc != 0) {
+ FL_ERR("AST: Failed waiting for config write\n");
+ return rc;
+ }
+ FL_DBG("AST: Updated to : 0x%02x\n", vconf);
+
+ /*
+ * Try to do full dual IO, with 8 dummy cycles it supports 133Mhz
+ *
+ * The CE# inactive width for reads must be 20ns, we set it
+ * to 4T which is about 20.8ns.
+ */
+ ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x03 << 28) | /* Single bit */
+ (0x0c << 24) | /* CE# 4T */
+ (0xbb << 16) | /* 2READ command */
+ (0x00 << 8) | /* HCLK/16 (optimize later) */
+ (0x02 << 6) | /* 8 dummy cycles (2 bytes) */
+ (0x01); /* fast read */
+
+ /* Configure SPI flash read timing */
+ rc = ast_sf_optimize_reads(ct, info, 133000000);
+ if (rc) {
+ FL_ERR("AST: Failed to setup proper read timings, rc=%d\n", rc);
+ return rc;
+ }
+
+ /*
+ * For other commands and writes also increase the SPI clock
+ * to HCLK/2 since the chip supports up to 133Mhz. CE# inactive
+ * for write and erase is 50ns so let's set it to 10T.
+ */
+ ct->ctl_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x06 << 24) | /* CE# width 10T (b0110) */
+ (0x00 << 16) | /* no command */
+ (0x00 << 8) | /* HCLK/16 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* norm read */
+
+ div = ast_sf_get_hclk(&ct->ctl_val, 133000000);
+ FL_DBG("AST: Command timing set to HCLK/%d\n", div);
+
+ /* Update chip with current read config */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ return 0;
+
+ dumb:
+ ct->ctl_val = ct->ctl_read_val = (ct->ctl_read_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# max */
+ (0x03 << 16) | /* use normal reads */
+ (0x06 << 8) | /* HCLK/4 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ /* Update chip with current read config */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ return 0;
+}
+
+static int ast_sf_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ struct flash_info *info = ctrl->finfo;
+
+ (void)tsize;
+
+ /*
+ * Configure better timings and read mode for known
+ * flash chips
+ */
+ switch(info->id) {
+ case 0xc22018: /* MX25L12835F */
+ case 0xc22019: /* MX25L25635F */
+ case 0xc2201a: /* MX66L51235F */
+ return ast_sf_setup_macronix(ct, info);
+ case 0xef4018: /* W25Q128BV */
+ return ast_sf_setup_winbond(ct, info);
+ case 0x20ba20: /* MT25Qx512xx */
+ return ast_sf_setup_micron(ct, info);
+ }
+ /* No special tuning */
+ return 0;
+}
+
+static bool ast_sf_init_pnor(struct ast_sf_ctrl *ct)
+{
+ uint32_t reg;
+
+ ct->ctl_reg = PNOR_SPI_FCTL_CTRL;
+ ct->fread_timing_reg = PNOR_SPI_FREAD_TIMING;
+ ct->flash = PNOR_FLASH_BASE;
+
+ /* Enable writing to the controller */
+ reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF);
+ if (reg == 0xffffffff) {
+ FL_ERR("AST_SF: Failed read from controller config\n");
+ return false;
+ }
+ ast_ahb_writel(reg | 1, PNOR_SPI_FCTL_CONF);
+
+ /*
+ * Snapshot control reg and sanitize it for our
+ * use, switching to 1-bit mode, clearing user
+ * mode if set, etc...
+ *
+ * Also configure SPI clock to something safe
+ * like HCLK/8 (24Mhz)
+ */
+ ct->ctl_val = ast_ahb_readl(ct->ctl_reg);
+ if (ct->ctl_val == 0xffffffff) {
+ FL_ERR("AST_SF: Failed read from controller control\n");
+ return false;
+ }
+
+ ct->ctl_val = (ct->ctl_val & 0x2000) |
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# width 16T */
+ (0x00 << 16) | /* no command */
+ (0x04 << 8) | /* HCLK/8 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ /* Initial read mode is default */
+ ct->ctl_read_val = ct->ctl_val;
+
+ /* Initial read timings all 0 */
+ ct->fread_timing_val = 0;
+
+ /* Configure for read */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
+
+ if (ct->ctl_val & 0x2000)
+ ct->mode_4b = true;
+ else
+ ct->mode_4b = false;
+
+ return true;
+}
+
+static bool ast_sf_init_bmc(struct ast_sf_ctrl *ct)
+{
+ ct->ctl_reg = BMC_SPI_FCTL_CTRL;
+ ct->fread_timing_reg = BMC_SPI_FREAD_TIMING;
+ ct->flash = BMC_FLASH_BASE;
+
+ /*
+ * Snapshot control reg and sanitize it for our
+ * use, switching to 1-bit mode, clearing user
+ * mode if set, etc...
+ *
+ * Also configure SPI clock to something safe
+ * like HCLK/8 (24Mhz)
+ */
+ ct->ctl_val =
+ (0x00 << 28) | /* Single bit */
+ (0x00 << 24) | /* CE# width 16T */
+ (0x00 << 16) | /* no command */
+ (0x04 << 8) | /* HCLK/8 */
+ (0x00 << 6) | /* no dummy cycle */
+ (0x00); /* normal read */
+
+ /* Initial read mode is default */
+ ct->ctl_read_val = ct->ctl_val;
+
+ /* Initial read timings all 0 */
+ ct->fread_timing_val = 0;
+
+ /* Configure for read */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+ ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg);
+
+ ct->mode_4b = false;
+
+ return true;
+}
+
+static int ast_mem_set4b(struct spi_flash_ctrl *ctrl __unused,
+ bool enable __unused)
+{
+ return 0;
+}
+
+static int ast_mem_setup(struct spi_flash_ctrl *ctrl __unused,
+ uint32_t *tsize __unused)
+{
+ return 0;
+}
+
+static int ast_mem_chipid(struct spi_flash_ctrl *ctrl __unused, uint8_t *id_buf,
+ uint32_t *id_size)
+{
+ if (*id_size < 3)
+ return -1;
+
+ id_buf[0] = 0xaa;
+ id_buf[1] = 0x55;
+ id_buf[2] = 0xaa;
+ *id_size = 3;
+ return 0;
+}
+
+static int ast_mem_write(struct spi_flash_ctrl *ctrl, uint32_t pos,
+ const void *buf, uint32_t len)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+
+ /*
+ * This only works when the ahb is pointed at system memory.
+ */
+ return ast_copy_to_ahb(ct->flash + pos, buf, len);
+}
+
+static int ast_mem_erase(struct spi_flash_ctrl *ctrl, uint32_t addr, uint32_t size)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+ uint32_t pos, len, end = addr + size;
+ uint64_t zero = 0;
+ int ret;
+
+ for (pos = addr; pos < end; pos += sizeof(zero)) {
+ if (pos + sizeof(zero) > end)
+ len = end - pos;
+ else
+ len = sizeof(zero);
+
+ ret = ast_copy_to_ahb(ct->flash + pos, &zero, len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl)
+{
+ struct ast_sf_ctrl *ct;
+
+ if (type != AST_SF_TYPE_PNOR && type != AST_SF_TYPE_BMC
+ && type != AST_SF_TYPE_MEM)
+ return -EINVAL;
+
+ *ctrl = NULL;
+ ct = malloc(sizeof(*ct));
+ if (!ct) {
+ FL_ERR("AST_SF: Failed to allocate\n");
+ return -ENOMEM;
+ }
+ memset(ct, 0, sizeof(*ct));
+ ct->type = type;
+
+ if (type == AST_SF_TYPE_MEM) {
+ ct->ops.cmd_wr = NULL;
+ ct->ops.cmd_rd = NULL;
+ ct->ops.read = ast_sf_read;
+ ct->ops.set_4b = ast_mem_set4b;
+ ct->ops.write = ast_mem_write;
+ ct->ops.erase = ast_mem_erase;
+ ct->ops.setup = ast_mem_setup;
+ ct->ops.chip_id = ast_mem_chipid;
+ ct->flash = PNOR_FLASH_BASE;
+ } else {
+ ct->ops.cmd_wr = ast_sf_cmd_wr;
+ ct->ops.cmd_rd = ast_sf_cmd_rd;
+ ct->ops.set_4b = ast_sf_set_4b;
+ ct->ops.read = ast_sf_read;
+ ct->ops.setup = ast_sf_setup;
+ }
+
+ ast_get_ahb_freq();
+
+ if (type == AST_SF_TYPE_PNOR) {
+ if (!ast_sf_init_pnor(ct))
+ goto fail;
+ } else if (type == AST_SF_TYPE_BMC) {
+ if (!ast_sf_init_bmc(ct))
+ goto fail;
+ }
+
+ *ctrl = &ct->ops;
+
+ return 0;
+ fail:
+ free(ct);
+ return -EIO;
+}
+
+void ast_sf_close(struct spi_flash_ctrl *ctrl)
+{
+ struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops);
+
+ /* Restore control reg to read */
+ ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg);
+
+ /* Additional cleanup */
+ if (ct->type == AST_SF_TYPE_PNOR) {
+ uint32_t reg = ast_ahb_readl(PNOR_SPI_FCTL_CONF);
+ if (reg != 0xffffffff)
+ ast_ahb_writel(reg & ~1, PNOR_SPI_FCTL_CONF);
+ }
+
+ /* Free the whole lot */
+ free(ct);
+}
diff --git a/op-flasher/pflash/ast.h b/op-flasher/pflash/ast.h
new file mode 100644
index 0000000..c7bf0cb
--- /dev/null
+++ b/op-flasher/pflash/ast.h
@@ -0,0 +1,102 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __AST_H
+#define __AST_H
+
+/*
+ * AHB bus registers
+ */
+
+/* SPI Flash controller #1 (BMC) */
+#define BMC_SPI_FCTL_BASE 0x1E620000
+#define BMC_SPI_FCTL_CE_CTRL (BMC_SPI_FCTL_BASE + 0x04)
+#define BMC_SPI_FCTL_CTRL (BMC_SPI_FCTL_BASE + 0x10)
+#define BMC_SPI_FREAD_TIMING (BMC_SPI_FCTL_BASE + 0x94)
+#define BMC_FLASH_BASE 0x20000000
+
+/* SPI Flash controller #2 (PNOR) */
+#define PNOR_SPI_FCTL_BASE 0x1E630000
+#define PNOR_SPI_FCTL_CONF (PNOR_SPI_FCTL_BASE + 0x00)
+#define PNOR_SPI_FCTL_CTRL (PNOR_SPI_FCTL_BASE + 0x04)
+#define PNOR_SPI_FREAD_TIMING (PNOR_SPI_FCTL_BASE + 0x14)
+#define PNOR_FLASH_BASE 0x30000000
+
+/* LPC registers */
+#define LPC_BASE 0x1e789000
+#define LPC_HICR6 (LPC_BASE + 0x80)
+#define LPC_HICR7 (LPC_BASE + 0x88)
+#define LPC_HICR8 (LPC_BASE + 0x8c)
+#define LPC_iBTCR0 (LPC_BASE + 0x140)
+
+/* VUART1 */
+#define VUART1_BASE 0x1e787000
+#define VUART1_GCTRLA (VUART1_BASE + 0x20)
+#define VUART1_GCTRLB (VUART1_BASE + 0x24)
+#define VUART1_ADDRL (VUART1_BASE + 0x28)
+#define VUART1_ADDRH (VUART1_BASE + 0x2c)
+
+/* SCU registers */
+#define SCU_BASE 0x1e6e2000
+#define SCU_HW_STRAPPING (SCU_BASE + 0x70)
+
+/*
+ * AHB Accessors
+ */
+#ifndef __SKIBOOT__
+#include "io.h"
+#else
+
+/*
+ * Register accessors, return byteswapped values
+ * (IE. LE registers)
+ */
+void ast_ahb_writel(uint32_t val, uint32_t reg);
+uint32_t ast_ahb_readl(uint32_t reg);
+
+/*
+ * copy to/from accessors. Cannot cross IDSEL boundaries (256M)
+ */
+int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len);
+int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len);
+
+void ast_io_init(void);
+bool ast_is_ahb_lpc_pnor(void);
+
+/* UART configuration */
+
+bool ast_is_vuart1_enabled(void);
+void ast_setup_vuart1(uint16_t io_base, uint8_t irq);
+void ast_setup_sio_uart1(uint16_t io_base, uint8_t irq);
+void ast_disable_sio_uart1(void);
+
+/* BT configuration */
+void ast_setup_ibt(uint16_t io_base, uint8_t irq);
+
+#endif /* __SKIBOOT__ */
+
+/*
+ * SPI Flash controllers
+ */
+#define AST_SF_TYPE_PNOR 0
+#define AST_SF_TYPE_BMC 1
+#define AST_SF_TYPE_MEM 2
+
+struct spi_flash_ctrl;
+int ast_sf_open(uint8_t type, struct spi_flash_ctrl **ctrl);
+void ast_sf_close(struct spi_flash_ctrl *ctrl);
+
+
+#endif /* __AST_H */
diff --git a/op-flasher/pflash/ccan/Makefile.inc b/op-flasher/pflash/ccan/Makefile.inc
new file mode 100644
index 0000000..b132f9d
--- /dev/null
+++ b/op-flasher/pflash/ccan/Makefile.inc
@@ -0,0 +1,8 @@
+# -*-Makefile-*-
+
+SUBDIRS += ccan ccan/list ccan/str
+CCAN_OBJS = list/list.o str/str.o
+CCAN=ccan/built-in.o
+
+$(CCAN): $(CCAN_OBJS:%=ccan/%)
+
diff --git a/op-flasher/pflash/ccan/array_size/LICENSE b/op-flasher/pflash/ccan/array_size/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/op-flasher/pflash/ccan/array_size/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/op-flasher/pflash/ccan/array_size/_info b/op-flasher/pflash/ccan/array_size/_info
new file mode 100644
index 0000000..d670042
--- /dev/null
+++ b/op-flasher/pflash/ccan/array_size/_info
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * array_size - routine for safely deriving the size of a visible array.
+ *
+ * This provides a simple ARRAY_SIZE() macro, which (given a good compiler)
+ * will also break compile if you try to use it on a pointer.
+ *
+ * This can ensure your code is robust to changes, without needing a gratuitous
+ * macro or constant.
+ *
+ * Example:
+ * // Outputs "Initialized 32 values"
+ * #include <ccan/array_size/array_size.h>
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * // We currently use 32 random values.
+ * static unsigned int vals[32];
+ *
+ * int main(void)
+ * {
+ * unsigned int i;
+ * for (i = 0; i < ARRAY_SIZE(vals); i++)
+ * vals[i] = random();
+ * printf("Initialized %u values\n", i);
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/build_assert\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/op-flasher/pflash/ccan/array_size/array_size.h b/op-flasher/pflash/ccan/array_size/array_size.h
new file mode 100644
index 0000000..3279dc5
--- /dev/null
+++ b/op-flasher/pflash/ccan/array_size/array_size.h
@@ -0,0 +1,26 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_ARRAY_SIZE_H
+#define CCAN_ARRAY_SIZE_H
+#include "config.h"
+#include <ccan/build_assert/build_assert.h>
+
+/**
+ * ARRAY_SIZE - get the number of elements in a visible array
+ * @arr: the array whose size you want.
+ *
+ * This does not work on pointers, or arrays declared as [], or
+ * function parameters. With correct compiler support, such usage
+ * will cause a build error (see build_assert).
+ */
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr))
+
+#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF
+/* Two gcc extensions.
+ * &a[0] degrades to a pointer: a different type from an array */
+#define _array_size_chk(arr) \
+ BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(arr), \
+ typeof(&(arr)[0])))
+#else
+#define _array_size_chk(arr) 0
+#endif
+#endif /* CCAN_ALIGNOF_H */
diff --git a/op-flasher/pflash/ccan/array_size/test/compile_fail-function-param.c b/op-flasher/pflash/ccan/array_size/test/compile_fail-function-param.c
new file mode 100644
index 0000000..ec4697d
--- /dev/null
+++ b/op-flasher/pflash/ccan/array_size/test/compile_fail-function-param.c
@@ -0,0 +1,24 @@
+#include <ccan/array_size/array_size.h>
+#include <stdlib.h>
+
+struct foo {
+ unsigned int a, b;
+};
+
+int check_parameter(const struct foo array[4]);
+int check_parameter(const struct foo array[4])
+{
+#ifdef FAIL
+ return (ARRAY_SIZE(array) == 4);
+#if !HAVE_TYPEOF || !HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if _array_size_chk is a noop."
+#endif
+#else
+ return sizeof(array) == 4 * sizeof(struct foo);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ return check_parameter(NULL);
+}
diff --git a/op-flasher/pflash/ccan/array_size/test/compile_fail.c b/op-flasher/pflash/ccan/array_size/test/compile_fail.c
new file mode 100644
index 0000000..0ba493d
--- /dev/null
+++ b/op-flasher/pflash/ccan/array_size/test/compile_fail.c
@@ -0,0 +1,14 @@
+#include <ccan/array_size/array_size.h>
+
+int main(int argc, char *argv[8])
+{
+ char array[100];
+#ifdef FAIL
+ return ARRAY_SIZE(argv) + ARRAY_SIZE(array);
+#if !HAVE_TYPEOF || !HAVE_BUILTIN_TYPES_COMPATIBLE_P
+#error "Unfortunately we don't fail if _array_size_chk is a noop."
+#endif
+#else
+ return ARRAY_SIZE(array);
+#endif
+}
diff --git a/op-flasher/pflash/ccan/array_size/test/run.c b/op-flasher/pflash/ccan/array_size/test/run.c
new file mode 100644
index 0000000..b42dd72
--- /dev/null
+++ b/op-flasher/pflash/ccan/array_size/test/run.c
@@ -0,0 +1,33 @@
+#include <ccan/array_size/array_size.h>
+#include <ccan/tap/tap.h>
+
+static char array1[1];
+static int array2[2];
+static unsigned long array3[3][5];
+struct foo {
+ unsigned int a, b;
+ char string[100];
+};
+static struct foo array4[4];
+
+/* Make sure they can be used in initializers. */
+static int array1_size = ARRAY_SIZE(array1);
+static int array2_size = ARRAY_SIZE(array2);
+static int array3_size = ARRAY_SIZE(array3);
+static int array4_size = ARRAY_SIZE(array4);
+
+int main(int argc, char *argv[])
+{
+ plan_tests(8);
+ ok1(array1_size == 1);
+ ok1(array2_size == 2);
+ ok1(array3_size == 3);
+ ok1(array4_size == 4);
+
+ ok1(ARRAY_SIZE(array1) == 1);
+ ok1(ARRAY_SIZE(array2) == 2);
+ ok1(ARRAY_SIZE(array3) == 3);
+ ok1(ARRAY_SIZE(array4) == 4);
+
+ return exit_status();
+}
diff --git a/op-flasher/pflash/ccan/build_assert/LICENSE b/op-flasher/pflash/ccan/build_assert/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/op-flasher/pflash/ccan/build_assert/_info b/op-flasher/pflash/ccan/build_assert/_info
new file mode 100644
index 0000000..bce92b7
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/_info
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * build_assert - routines for build-time assertions
+ *
+ * This code provides routines which will cause compilation to fail should some
+ * assertion be untrue: such failures are preferable to run-time assertions,
+ * but much more limited since they can only depends on compile-time constants.
+ *
+ * These assertions are most useful when two parts of the code must be kept in
+ * sync: it is better to avoid such cases if possible, but seconds best is to
+ * detect invalid changes at build time.
+ *
+ * For example, a tricky piece of code might rely on a certain element being at
+ * the start of the structure. To ensure that future changes don't break it,
+ * you would catch such changes in your code like so:
+ *
+ * Example:
+ * #include <stddef.h>
+ * #include <ccan/build_assert/build_assert.h>
+ *
+ * struct foo {
+ * char string[5];
+ * int x;
+ * };
+ *
+ * static char *foo_string(struct foo *foo)
+ * {
+ * // This trick requires that the string be first in the structure
+ * BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ * return (char *)foo;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ /* Nothing. */
+ return 0;
+
+ return 1;
+}
diff --git a/op-flasher/pflash/ccan/build_assert/build_assert.h b/op-flasher/pflash/ccan/build_assert/build_assert.h
new file mode 100644
index 0000000..bd07254
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/build_assert.h
@@ -0,0 +1,40 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_BUILD_ASSERT_H
+#define CCAN_BUILD_ASSERT_H
+
+/**
+ * BUILD_ASSERT - assert a build-time dependency.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler. This can only be used within a function.
+ *
+ * Example:
+ * #include <stddef.h>
+ * ...
+ * static char *foo_to_char(struct foo *foo)
+ * {
+ * // This code needs string to be at start of foo.
+ * BUILD_ASSERT(offsetof(struct foo, string) == 0);
+ * return (char *)foo;
+ * }
+ */
+#define BUILD_ASSERT(cond) \
+ do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
+
+/**
+ * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
+ * @cond: the compile-time condition which must be true.
+ *
+ * Your compile will fail if the condition isn't true, or can't be evaluated
+ * by the compiler. This can be used in an expression: its value is "0".
+ *
+ * Example:
+ * #define foo_to_char(foo) \
+ * ((char *)(foo) \
+ * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
+ */
+#define BUILD_ASSERT_OR_ZERO(cond) \
+ (sizeof(char [1 - 2*!(cond)]) - 1)
+
+#endif /* CCAN_BUILD_ASSERT_H */
diff --git a/op-flasher/pflash/ccan/build_assert/test/compile_fail-expr.c b/op-flasher/pflash/ccan/build_assert/test/compile_fail-expr.c
new file mode 100644
index 0000000..1c6e31c
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/test/compile_fail-expr.c
@@ -0,0 +1,10 @@
+#include <ccan/build_assert/build_assert.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+ return BUILD_ASSERT_OR_ZERO(1 == 0);
+#else
+ return 0;
+#endif
+}
diff --git a/op-flasher/pflash/ccan/build_assert/test/compile_fail.c b/op-flasher/pflash/ccan/build_assert/test/compile_fail.c
new file mode 100644
index 0000000..f29c3fd
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/test/compile_fail.c
@@ -0,0 +1,9 @@
+#include <ccan/build_assert/build_assert.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+ BUILD_ASSERT(1 == 0);
+#endif
+ return 0;
+}
diff --git a/op-flasher/pflash/ccan/build_assert/test/compile_ok.c b/op-flasher/pflash/ccan/build_assert/test/compile_ok.c
new file mode 100644
index 0000000..20c2af6
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/test/compile_ok.c
@@ -0,0 +1,7 @@
+#include <ccan/build_assert/build_assert.h>
+
+int main(int argc, char *argv[])
+{
+ BUILD_ASSERT(1 == 1);
+ return 0;
+}
diff --git a/op-flasher/pflash/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c b/op-flasher/pflash/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c
new file mode 100644
index 0000000..a5450f1
--- /dev/null
+++ b/op-flasher/pflash/ccan/build_assert/test/run-BUILD_ASSERT_OR_ZERO.c
@@ -0,0 +1,9 @@
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/tap/tap.h>
+
+int main(int argc, char *argv[])
+{
+ plan_tests(1);
+ ok1(BUILD_ASSERT_OR_ZERO(1 == 1) == 0);
+ return exit_status();
+}
diff --git a/op-flasher/pflash/ccan/built-in.o b/op-flasher/pflash/ccan/built-in.o
new file mode 100644
index 0000000..510b477
--- /dev/null
+++ b/op-flasher/pflash/ccan/built-in.o
Binary files differ
diff --git a/op-flasher/pflash/ccan/check_type/LICENSE b/op-flasher/pflash/ccan/check_type/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/op-flasher/pflash/ccan/check_type/_info b/op-flasher/pflash/ccan/check_type/_info
new file mode 100644
index 0000000..cb19e20
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/_info
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * check_type - routines for compile time type checking
+ *
+ * C has fairly weak typing: ints get automatically converted to longs, signed
+ * to unsigned, etc. There are some cases where this is best avoided, and
+ * these macros provide methods for evoking warnings (or build errors) when
+ * a precise type isn't used.
+ *
+ * On compilers which don't support typeof() these routines are less effective,
+ * since they have to use sizeof() which can only distiguish between types of
+ * different size.
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+#if !HAVE_TYPEOF
+ printf("ccan/build_assert\n");
+#endif
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/op-flasher/pflash/ccan/check_type/check_type.h b/op-flasher/pflash/ccan/check_type/check_type.h
new file mode 100644
index 0000000..ef4a9b3
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/check_type.h
@@ -0,0 +1,64 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_CHECK_TYPE_H
+#define CCAN_CHECK_TYPE_H
+#include "config.h"
+
+/**
+ * check_type - issue a warning or build failure if type is not correct.
+ * @expr: the expression whose type we should check (not evaluated).
+ * @type: the exact type we expect the expression to be.
+ *
+ * This macro is usually used within other macros to try to ensure that a macro
+ * argument is of the expected type. No type promotion of the expression is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_type() always evaluates to 0.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ * // They should always pass a 64-bit value to _set_some_value!
+ * #define set_some_value(expr) \
+ * _set_some_value((check_type((expr), uint64_t), (expr)))
+ */
+
+/**
+ * check_types_match - issue a warning or build failure if types are not same.
+ * @expr1: the first expression (not evaluated).
+ * @expr2: the second expression (not evaluated).
+ *
+ * This macro is usually used within other macros to try to ensure that
+ * arguments are of identical types. No type promotion of the expressions is
+ * done: an unsigned int is not the same as an int!
+ *
+ * check_types_match() always evaluates to 0.
+ *
+ * If your compiler does not support typeof, then the best we can do is fail
+ * to compile if the sizes of the types are unequal (a less complete check).
+ *
+ * Example:
+ * // Do subtraction to get to enclosing type, but make sure that
+ * // pointer is of correct type for that member.
+ * #define container_of(mbr_ptr, encl_type, mbr) \
+ * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \
+ * ((encl_type *) \
+ * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))
+ */
+#if HAVE_TYPEOF
+#define check_type(expr, type) \
+ ((typeof(expr) *)0 != (type *)0)
+
+#define check_types_match(expr1, expr2) \
+ ((typeof(expr1) *)0 != (typeof(expr2) *)0)
+#else
+#include <ccan/build_assert/build_assert.h>
+/* Without typeof, we can only test the sizes. */
+#define check_type(expr, type) \
+ BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type))
+
+#define check_types_match(expr1, expr2) \
+ BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2))
+#endif /* HAVE_TYPEOF */
+
+#endif /* CCAN_CHECK_TYPE_H */
diff --git a/op-flasher/pflash/ccan/check_type/test/compile_fail-check_type.c b/op-flasher/pflash/ccan/check_type/test/compile_fail-check_type.c
new file mode 100644
index 0000000..00dad58
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/test/compile_fail-check_type.c
@@ -0,0 +1,9 @@
+#include <ccan/check_type/check_type.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+ check_type(argc, char);
+#endif
+ return 0;
+}
diff --git a/op-flasher/pflash/ccan/check_type/test/compile_fail-check_type_unsigned.c b/op-flasher/pflash/ccan/check_type/test/compile_fail-check_type_unsigned.c
new file mode 100644
index 0000000..8c0227a
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/test/compile_fail-check_type_unsigned.c
@@ -0,0 +1,14 @@
+#include <ccan/check_type/check_type.h>
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+#if HAVE_TYPEOF
+ check_type(argc, unsigned int);
+#else
+ /* This doesn't work without typeof, so just fail */
+#error "Fail without typeof"
+#endif
+#endif
+ return 0;
+}
diff --git a/op-flasher/pflash/ccan/check_type/test/compile_fail-check_types_match.c b/op-flasher/pflash/ccan/check_type/test/compile_fail-check_types_match.c
new file mode 100644
index 0000000..081304f
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/test/compile_fail-check_types_match.c
@@ -0,0 +1,10 @@
+#include <ccan/check_type/check_type.h>
+
+int main(int argc, char *argv[])
+{
+ unsigned char x = argc;
+#ifdef FAIL
+ check_types_match(argc, x);
+#endif
+ return x;
+}
diff --git a/op-flasher/pflash/ccan/check_type/test/run.c b/op-flasher/pflash/ccan/check_type/test/run.c
new file mode 100644
index 0000000..eb99632
--- /dev/null
+++ b/op-flasher/pflash/ccan/check_type/test/run.c
@@ -0,0 +1,22 @@
+#include <ccan/check_type/check_type.h>
+#include <ccan/tap/tap.h>
+
+int main(int argc, char *argv[])
+{
+ int x = 0, y = 0;
+
+ plan_tests(9);
+
+ ok1(check_type(argc, int) == 0);
+ ok1(check_type(&argc, int *) == 0);
+ ok1(check_types_match(argc, argc) == 0);
+ ok1(check_types_match(argc, x) == 0);
+ ok1(check_types_match(&argc, &x) == 0);
+
+ ok1(check_type(x++, int) == 0);
+ ok(x == 0, "check_type does not evaluate expression");
+ ok1(check_types_match(x++, y++) == 0);
+ ok(x == 0 && y == 0, "check_types_match does not evaluate expressions");
+
+ return exit_status();
+}
diff --git a/op-flasher/pflash/ccan/container_of/LICENSE b/op-flasher/pflash/ccan/container_of/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/op-flasher/pflash/ccan/container_of/_info b/op-flasher/pflash/ccan/container_of/_info
new file mode 100644
index 0000000..77b3bd1
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/_info
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * container_of - routine for upcasting
+ *
+ * It is often convenient to create code where the caller registers a pointer
+ * to a generic structure and a callback. The callback might know that the
+ * pointer points to within a larger structure, and container_of gives a
+ * convenient and fairly type-safe way of returning to the enclosing structure.
+ *
+ * This idiom is an alternative to providing a void * pointer for every
+ * callback.
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <ccan/container_of/container_of.h>
+ *
+ * struct timer {
+ * void *members;
+ * };
+ *
+ * struct info {
+ * int my_stuff;
+ * struct timer timer;
+ * };
+ *
+ * static void register_timer(struct timer *timer)
+ * {
+ * //...
+ * }
+ *
+ * static void my_timer_callback(struct timer *timer)
+ * {
+ * struct info *info = container_of(timer, struct info, timer);
+ * printf("my_stuff is %u\n", info->my_stuff);
+ * }
+ *
+ * int main(void)
+ * {
+ * struct info info = { .my_stuff = 1 };
+ *
+ * register_timer(&info.timer);
+ * // ...
+ * return 0;
+ * }
+ *
+ * License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/check_type\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/op-flasher/pflash/ccan/container_of/container_of.h b/op-flasher/pflash/ccan/container_of/container_of.h
new file mode 100644
index 0000000..bc53dfb
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/container_of.h
@@ -0,0 +1,109 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_CONTAINER_OF_H
+#define CCAN_CONTAINER_OF_H
+#include <stddef.h>
+
+#include "config.h"
+#include <ccan/check_type/check_type.h>
+
+/**
+ * container_of - get pointer to enclosing structure
+ * @member_ptr: pointer to the structure member
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info(struct foo *foo)
+ * {
+ * return container_of(foo, struct info, my_foo);
+ * }
+ */
+#define container_of(member_ptr, containing_type, member) \
+ ((containing_type *) \
+ ((char *)(member_ptr) \
+ - container_off(containing_type, member)) \
+ + check_types_match(*(member_ptr), ((containing_type *)0)->member))
+
+/**
+ * container_off - get offset to enclosing structure
+ * @containing_type: the type this member is within
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does
+ * typechecking and figures out the offset to the enclosing type.
+ *
+ * Example:
+ * struct foo {
+ * int fielda, fieldb;
+ * // ...
+ * };
+ * struct info {
+ * int some_other_field;
+ * struct foo my_foo;
+ * };
+ *
+ * static struct info *foo_to_info(struct foo *foo)
+ * {
+ * size_t off = container_off(struct info, my_foo);
+ * return (void *)((char *)foo - off);
+ * }
+ */
+#define container_off(containing_type, member) \
+ offsetof(containing_type, member)
+
+/**
+ * container_of_var - get pointer to enclosing structure using a variable
+ * @member_ptr: pointer to the structure member
+ * @container_var: a pointer of same type as this member's container
+ * @member: the name of this member within the structure.
+ *
+ * Given a pointer to a member of a structure, this macro does pointer
+ * subtraction to return the pointer to the enclosing type.
+ *
+ * Example:
+ * static struct info *foo_to_i(struct foo *foo)
+ * {
+ * struct info *i = container_of_var(foo, i, my_foo);
+ * return i;
+ * }
+ */
+#if HAVE_TYPEOF
+#define container_of_var(member_ptr, container_var, member) \
+ container_of(member_ptr, typeof(*container_var), member)
+#else
+#define container_of_var(member_ptr, container_var, member) \
+ ((void *)((char *)(member_ptr) - \
+ container_off_var(container_var, member)))
+#endif
+
+/**
+ * container_off_var - get offset of a field in enclosing structure
+ * @container_var: a pointer to a container structure
+ * @member: the name of a member within the structure.
+ *
+ * Given (any) pointer to a structure and a its member name, this
+ * macro does pointer subtraction to return offset of member in a
+ * structure memory layout.
+ *
+ */
+#if HAVE_TYPEOF
+#define container_off_var(var, member) \
+ container_off(typeof(*var), member)
+#else
+#define container_off_var(var, member) \
+ ((char *)&(var)->member - (char *)(var))
+#endif
+
+#endif /* CCAN_CONTAINER_OF_H */
diff --git a/op-flasher/pflash/ccan/container_of/test/compile_fail-bad-type.c b/op-flasher/pflash/ccan/container_of/test/compile_fail-bad-type.c
new file mode 100644
index 0000000..9ee8e9f
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/test/compile_fail-bad-type.c
@@ -0,0 +1,22 @@
+#include <ccan/container_of/container_of.h>
+#include <stdlib.h>
+
+struct foo {
+ int a;
+ char b;
+};
+
+int main(int argc, char *argv[])
+{
+ struct foo foo = { .a = 1, .b = 2 };
+ int *intp = &foo.a;
+ char *p;
+
+#ifdef FAIL
+ /* p is a char *, but this gives a struct foo * */
+ p = container_of(intp, struct foo, a);
+#else
+ p = (char *)intp;
+#endif
+ return p == NULL;
+}
diff --git a/op-flasher/pflash/ccan/container_of/test/compile_fail-types.c b/op-flasher/pflash/ccan/container_of/test/compile_fail-types.c
new file mode 100644
index 0000000..acd57c7
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/test/compile_fail-types.c
@@ -0,0 +1,22 @@
+#include <ccan/container_of/container_of.h>
+#include <stdlib.h>
+
+struct foo {
+ int a;
+ char b;
+};
+
+int main(int argc, char *argv[])
+{
+ struct foo foo = { .a = 1, .b = 2 }, *foop;
+ int *intp = &foo.a;
+
+#ifdef FAIL
+ /* b is a char, but intp is an int * */
+ foop = container_of(intp, struct foo, b);
+#else
+ foop = NULL;
+#endif
+ (void) foop; /* Suppress unused-but-set-variable warning. */
+ return intp == NULL;
+}
diff --git a/op-flasher/pflash/ccan/container_of/test/compile_fail-var-types.c b/op-flasher/pflash/ccan/container_of/test/compile_fail-var-types.c
new file mode 100644
index 0000000..60f25bd
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/test/compile_fail-var-types.c
@@ -0,0 +1,25 @@
+#include <ccan/container_of/container_of.h>
+#include <stdlib.h>
+
+struct foo {
+ int a;
+ char b;
+};
+
+int main(int argc, char *argv[])
+{
+ struct foo foo = { .a = 1, .b = 2 }, *foop;
+ int *intp = &foo.a;
+
+#ifdef FAIL
+ /* b is a char, but intp is an int * */
+ foop = container_of_var(intp, foop, b);
+#if !HAVE_TYPEOF
+#error "Unfortunately we don't fail if we don't have typeof."
+#endif
+#else
+ foop = NULL;
+#endif
+ (void) foop; /* Suppress unused-but-set-variable warning. */
+ return intp == NULL;
+}
diff --git a/op-flasher/pflash/ccan/container_of/test/run.c b/op-flasher/pflash/ccan/container_of/test/run.c
new file mode 100644
index 0000000..bc87789
--- /dev/null
+++ b/op-flasher/pflash/ccan/container_of/test/run.c
@@ -0,0 +1,24 @@
+#include <ccan/container_of/container_of.h>
+#include <ccan/tap/tap.h>
+
+struct foo {
+ int a;
+ char b;
+};
+
+int main(int argc, char *argv[])
+{
+ struct foo foo = { .a = 1, .b = 2 };
+ int *intp = &foo.a;
+ char *charp = &foo.b;
+
+ plan_tests(6);
+ ok1(container_of(intp, struct foo, a) == &foo);
+ ok1(container_of(charp, struct foo, b) == &foo);
+ ok1(container_of_var(intp, &foo, a) == &foo);
+ ok1(container_of_var(charp, &foo, b) == &foo);
+
+ ok1(container_off(struct foo, a) == 0);
+ ok1(container_off(struct foo, b) == offsetof(struct foo, b));
+ return exit_status();
+}
diff --git a/op-flasher/pflash/ccan/endian/LICENSE b/op-flasher/pflash/ccan/endian/LICENSE
new file mode 100644
index 0000000..feb9b11
--- /dev/null
+++ b/op-flasher/pflash/ccan/endian/LICENSE
@@ -0,0 +1,28 @@
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
+
+ the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
+ moral rights retained by the original author(s) and/or performer(s);
+ publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
+ rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
+ rights protecting the extraction, dissemination, use and reuse of data in a Work;
+ database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
+ other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
+ Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
+ Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
+ Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
diff --git a/op-flasher/pflash/ccan/endian/_info b/op-flasher/pflash/ccan/endian/_info
new file mode 100644
index 0000000..753afa7
--- /dev/null
+++ b/op-flasher/pflash/ccan/endian/_info
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * endian - endian conversion macros for simple types
+ *
+ * Portable protocols (such as on-disk formats, or network protocols)
+ * are often defined to be a particular endian: little-endian (least
+ * significant bytes first) or big-endian (most significant bytes
+ * first).
+ *
+ * Similarly, some CPUs lay out values in memory in little-endian
+ * order (most commonly, Intel's 8086 and derivatives), or big-endian
+ * order (almost everyone else).
+ *
+ * This module provides conversion routines, inspired by the linux kernel.
+ *
+ * Example:
+ * #include <stdio.h>
+ * #include <err.h>
+ * #include <ccan/endian/endian.h>
+ *
+ * //
+ * int main(int argc, char *argv[])
+ * {
+ * uint32_t value;
+ *
+ * if (argc != 2)
+ * errx(1, "Usage: %s <value>", argv[0]);
+ *
+ * value = atoi(argv[1]);
+ * printf("native: %08x\n", value);
+ * printf("little-endian: %08x\n", cpu_to_le32(value));
+ * printf("big-endian: %08x\n", cpu_to_be32(value));
+ * printf("byte-reversed: %08x\n", bswap_32(value));
+ * exit(0);
+ * }
+ *
+ * License: License: CC0 (Public domain)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ /* Nothing */
+ return 0;
+
+ return 1;
+}
diff --git a/op-flasher/pflash/ccan/endian/endian.h b/op-flasher/pflash/ccan/endian/endian.h
new file mode 100644
index 0000000..39c5308
--- /dev/null
+++ b/op-flasher/pflash/ccan/endian/endian.h
@@ -0,0 +1,314 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_ENDIAN_H
+#define CCAN_ENDIAN_H
+#include <stdint.h>
+#include "config.h"
+
+/**
+ * BSWAP_16 - reverse bytes in a constant uint16_t value.
+ * @val: constant value whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_16(0x1234)];
+ * };
+ */
+#define BSWAP_16(val) \
+ ((((uint16_t)val & 0x00ff) << 8) \
+ | (((uint16_t)val & 0xff00) >> 8))
+
+/**
+ * BSWAP_32 - reverse bytes in a constant uint32_t value.
+ * @val: constant value whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_32(0xff000000)];
+ * };
+ */
+#define BSWAP_32(val) \
+ ((((uint32_t)val & 0x000000ff) << 24) \
+ | (((uint32_t)val & 0x0000ff00) << 8) \
+ | (((uint32_t)val & 0x00ff0000) >> 8) \
+ | (((uint32_t)val & 0xff000000) >> 24))
+
+/**
+ * BSWAP_64 - reverse bytes in a constant uint64_t value.
+ * @val: constantvalue whose bytes to swap.
+ *
+ * Designed to be usable in constant-requiring initializers.
+ *
+ * Example:
+ * struct mystruct {
+ * char buf[BSWAP_64(0xff00000000000000ULL)];
+ * };
+ */
+#define BSWAP_64(val) \
+ ((((uint64_t)val & 0x00000000000000ffULL) << 56) \
+ | (((uint64_t)val & 0x000000000000ff00ULL) << 40) \
+ | (((uint64_t)val & 0x0000000000ff0000ULL) << 24) \
+ | (((uint64_t)val & 0x00000000ff000000ULL) << 8) \
+ | (((uint64_t)val & 0x000000ff00000000ULL) >> 8) \
+ | (((uint64_t)val & 0x0000ff0000000000ULL) >> 24) \
+ | (((uint64_t)val & 0x00ff000000000000ULL) >> 40) \
+ | (((uint64_t)val & 0xff00000000000000ULL) >> 56))
+
+#if HAVE_BYTESWAP_H
+#include <byteswap.h>
+#else
+/**
+ * bswap_16 - reverse bytes in a uint16_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 4 as two bytes reversed"
+ * printf("1024 is %u as two bytes reversed\n", bswap_16(1024));
+ */
+static inline uint16_t bswap_16(uint16_t val)
+{
+ return BSWAP_16(val);
+}
+
+/**
+ * bswap_32 - reverse bytes in a uint32_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 262144 as four bytes reversed"
+ * printf("1024 is %u as four bytes reversed\n", bswap_32(1024));
+ */
+static inline uint32_t bswap_32(uint32_t val)
+{
+ return BSWAP_32(val);
+}
+#endif /* !HAVE_BYTESWAP_H */
+
+#if !HAVE_BSWAP_64
+/**
+ * bswap_64 - reverse bytes in a uint64_t value.
+ * @val: value whose bytes to swap.
+ *
+ * Example:
+ * // Output contains "1024 is 1125899906842624 as eight bytes reversed"
+ * printf("1024 is %llu as eight bytes reversed\n",
+ * (unsigned long long)bswap_64(1024));
+ */
+static inline uint64_t bswap_64(uint64_t val)
+{
+ return BSWAP_64(val);
+}
+#endif
+
+/* Sanity check the defines. We don't handle weird endianness. */
+#if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN
+#error "Unknown endian"
+#elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN
+#error "Can't compile for both big and little endian."
+#endif
+
+#if HAVE_LITTLE_ENDIAN
+/**
+ * CPU_TO_LE64 - convert a constant uint64_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE64(native) (native)
+
+/**
+ * CPU_TO_LE32 - convert a constant uint32_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE32(native) (native)
+
+/**
+ * CPU_TO_LE16 - convert a constant uint16_t value to little-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_LE16(native) (native)
+
+/**
+ * LE64_TO_CPU - convert a little-endian uint64_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE64_TO_CPU(le_val) (le_val)
+
+/**
+ * LE32_TO_CPU - convert a little-endian uint32_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE32_TO_CPU(le_val) (le_val)
+
+/**
+ * LE16_TO_CPU - convert a little-endian uint16_t constant
+ * @le_val: little-endian constant to convert
+ */
+#define LE16_TO_CPU(le_val) (le_val)
+
+#else /* ... HAVE_BIG_ENDIAN */
+#define CPU_TO_LE64(native) BSWAP_64(native)
+#define CPU_TO_LE32(native) BSWAP_32(native)
+#define CPU_TO_LE16(native) BSWAP_16(native)
+#define LE64_TO_CPU(le_val) BSWAP_64(le_val)
+#define LE32_TO_CPU(le_val) BSWAP_32(le_val)
+#define LE16_TO_CPU(le_val) BSWAP_16(le_val)
+#endif /* HAVE_BIG_ENDIAN */
+
+#if HAVE_BIG_ENDIAN
+/**
+ * CPU_TO_BE64 - convert a constant uint64_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE64(native) (native)
+
+/**
+ * CPU_TO_BE32 - convert a constant uint32_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE32(native) (native)
+
+/**
+ * CPU_TO_BE16 - convert a constant uint16_t value to big-endian
+ * @native: constant to convert
+ */
+#define CPU_TO_BE16(native) (native)
+
+/**
+ * BE64_TO_CPU - convert a big-endian uint64_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE64_TO_CPU(le_val) (le_val)
+
+/**
+ * BE32_TO_CPU - convert a big-endian uint32_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE32_TO_CPU(le_val) (le_val)
+
+/**
+ * BE16_TO_CPU - convert a big-endian uint16_t constant
+ * @le_val: big-endian constant to convert
+ */
+#define BE16_TO_CPU(le_val) (le_val)
+
+#else /* ... HAVE_LITTLE_ENDIAN */
+#define CPU_TO_BE64(native) BSWAP_64(native)
+#define CPU_TO_BE32(native) BSWAP_32(native)
+#define CPU_TO_BE16(native) BSWAP_16(native)
+#define BE64_TO_CPU(le_val) BSWAP_64(le_val)
+#define BE32_TO_CPU(le_val) BSWAP_32(le_val)
+#define BE16_TO_CPU(le_val) BSWAP_16(le_val)
+#endif /* HAVE_LITTE_ENDIAN */
+
+
+/**
+ * cpu_to_le64 - convert a uint64_t value to little-endian
+ * @native: value to convert
+ */
+static inline uint64_t cpu_to_le64(uint64_t native)
+{
+ return CPU_TO_LE64(native);
+}
+
+/**
+ * cpu_to_le32 - convert a uint32_t value to little-endian
+ * @native: value to convert
+ */
+static inline uint32_t cpu_to_le32(uint32_t native)
+{
+ return CPU_TO_LE32(native);
+}
+
+/**
+ * cpu_to_le16 - convert a uint16_t value to little-endian
+ * @native: value to convert
+ */
+static inline uint16_t cpu_to_le16(uint16_t native)
+{
+ return CPU_TO_LE16(native);
+}
+
+/**
+ * le64_to_cpu - convert a little-endian uint64_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint64_t le64_to_cpu(uint64_t le_val)
+{
+ return LE64_TO_CPU(le_val);
+}
+
+/**
+ * le32_to_cpu - convert a little-endian uint32_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint32_t le32_to_cpu(uint32_t le_val)
+{
+ return LE32_TO_CPU(le_val);
+}
+
+/**
+ * le16_to_cpu - convert a little-endian uint16_t value
+ * @le_val: little-endian value to convert
+ */
+static inline uint16_t le16_to_cpu(uint16_t le_val)
+{
+ return LE16_TO_CPU(le_val);
+}
+
+/**
+ * cpu_to_be64 - convert a uint64_t value to big endian.
+ * @native: value to convert
+ */
+static inline uint64_t cpu_to_be64(uint64_t native)
+{
+ return CPU_TO_BE64(native);
+}
+
+/**
+ * cpu_to_be32 - convert a uint32_t value to big endian.
+ * @native: value to convert
+ */
+static inline uint32_t cpu_to_be32(uint32_t native)
+{
+ return CPU_TO_BE32(native);
+}
+
+/**
+ * cpu_to_be16 - convert a uint16_t value to big endian.
+ * @native: value to convert
+ */
+static inline uint16_t cpu_to_be16(uint16_t native)
+{
+ return CPU_TO_BE16(native);
+}
+
+/**
+ * be64_to_cpu - convert a big-endian uint64_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint64_t be64_to_cpu(uint64_t be_val)
+{
+ return BE64_TO_CPU(be_val);
+}
+
+/**
+ * be32_to_cpu - convert a big-endian uint32_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint32_t be32_to_cpu(uint32_t be_val)
+{
+ return BE32_TO_CPU(be_val);
+}
+
+/**
+ * be16_to_cpu - convert a big-endian uint16_t value
+ * @be_val: big-endian value to convert
+ */
+static inline uint16_t be16_to_cpu(uint16_t be_val)
+{
+ return BE16_TO_CPU(be_val);
+}
+
+#endif /* CCAN_ENDIAN_H */
diff --git a/op-flasher/pflash/ccan/endian/test/compile_ok-constant.c b/op-flasher/pflash/ccan/endian/test/compile_ok-constant.c
new file mode 100644
index 0000000..93b5a03
--- /dev/null
+++ b/op-flasher/pflash/ccan/endian/test/compile_ok-constant.c
@@ -0,0 +1,12 @@
+#include <ccan/endian/endian.h>
+
+struct foo {
+ char one[BSWAP_16(0xFF00)];
+ char two[BSWAP_32(0xFF000000)];
+ char three[BSWAP_64(0xFF00000000000000ULL)];
+};
+
+int main(void)
+{
+ return 0;
+}
diff --git a/op-flasher/pflash/ccan/endian/test/run.c b/op-flasher/pflash/ccan/endian/test/run.c
new file mode 100644
index 0000000..8604068
--- /dev/null
+++ b/op-flasher/pflash/ccan/endian/test/run.c
@@ -0,0 +1,106 @@
+#include <ccan/endian/endian.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ccan/tap/tap.h>
+
+int main(int argc, char *argv[])
+{
+ union {
+ uint64_t u64;
+ unsigned char u64_bytes[8];
+ } u64;
+ union {
+ uint32_t u32;
+ unsigned char u32_bytes[4];
+ } u32;
+ union {
+ uint16_t u16;
+ unsigned char u16_bytes[2];
+ } u16;
+
+ plan_tests(48);
+
+ /* Straight swap tests. */
+ u64.u64_bytes[0] = 0x00;
+ u64.u64_bytes[1] = 0x11;
+ u64.u64_bytes[2] = 0x22;
+ u64.u64_bytes[3] = 0x33;
+ u64.u64_bytes[4] = 0x44;
+ u64.u64_bytes[5] = 0x55;
+ u64.u64_bytes[6] = 0x66;
+ u64.u64_bytes[7] = 0x77;
+ u64.u64 = bswap_64(u64.u64);
+ ok1(u64.u64_bytes[7] == 0x00);
+ ok1(u64.u64_bytes[6] == 0x11);
+ ok1(u64.u64_bytes[5] == 0x22);
+ ok1(u64.u64_bytes[4] == 0x33);
+ ok1(u64.u64_bytes[3] == 0x44);
+ ok1(u64.u64_bytes[2] == 0x55);
+ ok1(u64.u64_bytes[1] == 0x66);
+ ok1(u64.u64_bytes[0] == 0x77);
+
+ u32.u32_bytes[0] = 0x00;
+ u32.u32_bytes[1] = 0x11;
+ u32.u32_bytes[2] = 0x22;
+ u32.u32_bytes[3] = 0x33;
+ u32.u32 = bswap_32(u32.u32);
+ ok1(u32.u32_bytes[3] == 0x00);
+ ok1(u32.u32_bytes[2] == 0x11);
+ ok1(u32.u32_bytes[1] == 0x22);
+ ok1(u32.u32_bytes[0] == 0x33);
+
+ u16.u16_bytes[0] = 0x00;
+ u16.u16_bytes[1] = 0x11;
+ u16.u16 = bswap_16(u16.u16);
+ ok1(u16.u16_bytes[1] == 0x00);
+ ok1(u16.u16_bytes[0] == 0x11);
+
+ /* Endian tests. */
+ u64.u64 = cpu_to_le64(0x0011223344556677ULL);
+ ok1(u64.u64_bytes[0] == 0x77);
+ ok1(u64.u64_bytes[1] == 0x66);
+ ok1(u64.u64_bytes[2] == 0x55);
+ ok1(u64.u64_bytes[3] == 0x44);
+ ok1(u64.u64_bytes[4] == 0x33);
+ ok1(u64.u64_bytes[5] == 0x22);
+ ok1(u64.u64_bytes[6] == 0x11);
+ ok1(u64.u64_bytes[7] == 0x00);
+ ok1(le64_to_cpu(u64.u64) == 0x0011223344556677ULL);
+
+ u64.u64 = cpu_to_be64(0x0011223344556677ULL);
+ ok1(u64.u64_bytes[7] == 0x77);
+ ok1(u64.u64_bytes[6] == 0x66);
+ ok1(u64.u64_bytes[5] == 0x55);
+ ok1(u64.u64_bytes[4] == 0x44);
+ ok1(u64.u64_bytes[3] == 0x33);
+ ok1(u64.u64_bytes[2] == 0x22);
+ ok1(u64.u64_bytes[1] == 0x11);
+ ok1(u64.u64_bytes[0] == 0x00);
+ ok1(be64_to_cpu(u64.u64) == 0x0011223344556677ULL);
+
+ u32.u32 = cpu_to_le32(0x00112233);
+ ok1(u32.u32_bytes[0] == 0x33);
+ ok1(u32.u32_bytes[1] == 0x22);
+ ok1(u32.u32_bytes[2] == 0x11);
+ ok1(u32.u32_bytes[3] == 0x00);
+ ok1(le32_to_cpu(u32.u32) == 0x00112233);
+
+ u32.u32 = cpu_to_be32(0x00112233);
+ ok1(u32.u32_bytes[3] == 0x33);
+ ok1(u32.u32_bytes[2] == 0x22);
+ ok1(u32.u32_bytes[1] == 0x11);
+ ok1(u32.u32_bytes[0] == 0x00);
+ ok1(be32_to_cpu(u32.u32) == 0x00112233);
+
+ u16.u16 = cpu_to_le16(0x0011);
+ ok1(u16.u16_bytes[0] == 0x11);
+ ok1(u16.u16_bytes[1] == 0x00);
+ ok1(le16_to_cpu(u16.u16) == 0x0011);
+
+ u16.u16 = cpu_to_be16(0x0011);
+ ok1(u16.u16_bytes[1] == 0x11);
+ ok1(u16.u16_bytes[0] == 0x00);
+ ok1(be16_to_cpu(u16.u16) == 0x0011);
+
+ exit(exit_status());
+}
diff --git a/op-flasher/pflash/config.h b/op-flasher/pflash/config.h
new file mode 100644
index 0000000..8af0bbe
--- /dev/null
+++ b/op-flasher/pflash/config.h
@@ -0,0 +1,19 @@
+/* For CCAN */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#define HAVE_TYPEOF 1
+#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HAVE_BIG_ENDIAN 0
+#define HAVE_LITTLE_ENDIAN 1
+#else
+#define HAVE_BIG_ENDIAN 1
+#define HAVE_LITTLE_ENDIAN 0
+#endif
+
+#define HAVE_BYTESWAP_H 1
+#define HAVE_BSWAP_64 1
diff --git a/op-flasher/pflash/get_arch.sh b/op-flasher/pflash/get_arch.sh
new file mode 100644
index 0000000..18a5cef
--- /dev/null
+++ b/op-flasher/pflash/get_arch.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+echo "#if defined(__powerpc__)
+echo -n ARCH_POWERPC
+#elif defined(__x86_64__) || defined(__i386__)
+echo -n ARCH_X86
+#elif defined(__arm__)
+echo -n ARCH_ARM
+#else
+echo -n ARCH_UNKNOWN
+#endif" | $1cpp | sh
+
diff --git a/op-flasher/pflash/io.h b/op-flasher/pflash/io.h
new file mode 100644
index 0000000..f855717
--- /dev/null
+++ b/op-flasher/pflash/io.h
@@ -0,0 +1,121 @@
+#ifndef __IO_H
+#define __IO_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <libflash/libflash.h>
+
+/* AST AHB register base */
+#define AHB_REGS_BASE 0x1E600000
+#define AHB_REGS_SIZE 0x00200000
+
+/* AST GPIO control regs */
+#define GPIO_CTRL_BASE 0x1E780000
+#define GPIO_CTRL_SIZE 0x1000
+
+/* AST AHB mapping of PNOR */
+#define PNOR_FLASH_BASE 0x30000000
+#define PNOR_FLASH_SIZE 0x04000000
+
+/* AST AHB mapping of BMC flash */
+#define BMC_FLASH_BASE 0x20000000
+#define BMC_FLASH_SIZE 0x04000000
+
+/* Address of flash mapping on LPC FW space */
+#define LPC_FLASH_BASE 0x0e000000
+#define LPC_CTRL_BASE 0x1e789000
+
+extern void open_devs(bool use_lpc, bool bmc_flash);
+extern bool set_wrprotect(bool protect);
+
+#ifdef __powerpc__
+
+extern void close_devs(void);
+
+/* AST access functions */
+extern uint32_t (*ast_ahb_readl)(uint32_t offset);
+extern void (*ast_ahb_writel)(uint32_t val, uint32_t offset);
+extern int (*ast_copy_to_ahb)(uint32_t reg, const void *src, uint32_t len);
+extern int (*ast_copy_from_ahb)(void *dst, uint32_t reg, uint32_t len);
+
+/* SFC LPC access functions (big endian) */
+extern int lpc_fw_write32(uint32_t val, uint32_t addr);
+extern int lpc_fw_read32(uint32_t *val, uint32_t addr);
+
+extern void check_platform(bool *has_sfc, bool *has_ast);
+
+#else
+
+static inline void close_devs(void) { }
+
+static inline uint8_t readb(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint8_t *)addr;
+}
+
+static inline uint16_t readw(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint16_t *)addr;
+}
+
+static inline uint32_t readl(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint32_t *)addr;
+}
+
+static inline void writeb(uint8_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint8_t *)addr = val;
+}
+
+static inline void writew(uint16_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint16_t *)addr = val;
+}
+
+static inline void writel(uint32_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint32_t *)addr = val;
+}
+
+/*
+ * AHB register and flash access
+ */
+
+extern void *ahb_reg_map;
+
+static inline uint32_t ast_ahb_readl(uint32_t offset)
+{
+ assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0);
+
+ return readl(ahb_reg_map + (offset - AHB_REGS_BASE));
+}
+
+static inline void ast_ahb_writel(uint32_t val, uint32_t offset)
+{
+ assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0);
+
+ writel(val, ahb_reg_map + (offset - AHB_REGS_BASE));
+}
+
+extern int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len);
+extern int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len);
+
+static inline void check_platform(bool *has_sfc, bool *has_ast)
+{
+ *has_sfc = false;
+ *has_ast = true;
+}
+
+#endif
+
+#endif /* __IO_H */
+
diff --git a/op-flasher/pflash/libflash/ffs.h b/op-flasher/pflash/libflash/ffs.h
new file mode 100644
index 0000000..36b0b00
--- /dev/null
+++ b/op-flasher/pflash/libflash/ffs.h
@@ -0,0 +1,144 @@
+/* IBM_PROLOG_BEGIN_TAG */
+/* This is an automatically generated prolog. */
+/* */
+/* $Source: src/usr/pnor/ffs.h $ */
+/* */
+/* IBM CONFIDENTIAL */
+/* */
+/* COPYRIGHT International Business Machines Corp. 2012,2013 */
+/* */
+/* p1 */
+/* */
+/* Object Code Only (OCO) source materials */
+/* Licensed Internal Code Source Materials */
+/* IBM HostBoot Licensed Internal Code */
+/* */
+/* The source code for this program is not published or otherwise */
+/* divested of its trade secrets, irrespective of what has been */
+/* deposited with the U.S. Copyright Office. */
+/* */
+/* Origin: 30 */
+/* */
+/* IBM_PROLOG_END_TAG */
+/*
+ * Copyright (c) International Business Machines Corp., 2012
+ *
+ * FSP Flash Structure
+ *
+ * This header defines the layout for the FSP Flash Structure.
+ */
+
+#ifndef __FFS_H__
+#define __FFS_H__
+
+/* Pull in the correct header depending on what is being built */
+#if defined(__KERNEL__)
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+/* The version of this partition implementation */
+#define FFS_VERSION_1 1
+
+/* Magic number for the partition header (ASCII 'PART') */
+#define FFS_MAGIC 0x50415254
+
+/* The maximum length of the partition name */
+#define PART_NAME_MAX 15
+
+/*
+ * Sizes of the data structures
+ */
+#define FFS_HDR_SIZE sizeof(struct ffs_hdr)
+#define FFS_ENTRY_SIZE sizeof(struct ffs_entry)
+
+/*
+ * Sizes of the data structures w/o checksum
+ */
+#define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t))
+#define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t))
+
+/* pid of logical partitions/containers */
+#define FFS_PID_TOPLEVEL 0xFFFFFFFF
+
+/*
+ * Type of image contained w/in partition
+ */
+enum type {
+ FFS_TYPE_DATA = 1,
+ FFS_TYPE_LOGICAL = 2,
+ FFS_TYPE_PARTITION = 3,
+};
+
+/*
+ * Flag bit definitions
+ */
+#define FFS_FLAGS_PROTECTED 0x0001
+#define FFS_FLAGS_U_BOOT_ENV 0x0002
+
+/*
+ * Number of user data words
+ */
+#define FFS_USER_WORDS 16
+
+/**
+ * struct ffs_entry - Partition entry
+ *
+ * @name: Opaque null terminated string
+ * @base: Starting offset of partition in flash (in hdr.block_size)
+ * @size: Partition size (in hdr.block_size)
+ * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel)
+ * @id: Partition entry ID [1..65536]
+ * @type: Describe type of partition
+ * @flags: Partition attributes (optional)
+ * @actual: Actual partition size (in bytes)
+ * @resvd: Reserved words for future use
+ * @user: User data (optional)
+ * @checksum: Partition entry checksum (includes all above)
+ */
+struct ffs_entry {
+ char name[PART_NAME_MAX + 1];
+ uint32_t base;
+ uint32_t size;
+ uint32_t pid;
+ uint32_t id;
+ uint32_t type;
+ uint32_t flags;
+ uint32_t actual;
+ uint32_t resvd[4];
+ struct {
+ uint32_t data[FFS_USER_WORDS];
+ } user;
+ uint32_t checksum;
+} __attribute__ ((packed));
+
+/**
+ * struct ffs_hdr - FSP Flash Structure header
+ *
+ * @magic: Eye catcher/corruption detector
+ * @version: Version of the structure
+ * @size: Size of partition table (in block_size)
+ * @entry_size: Size of struct ffs_entry element (in bytes)
+ * @entry_count: Number of struct ffs_entry elements in @entries array
+ * @block_size: Size of block on device (in bytes)
+ * @block_count: Number of blocks on device
+ * @resvd: Reserved words for future use
+ * @checksum: Header checksum
+ * @entries: Pointer to array of partition entries
+ */
+struct ffs_hdr {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t size;
+ uint32_t entry_size;
+ uint32_t entry_count;
+ uint32_t block_size;
+ uint32_t block_count;
+ uint32_t resvd[4];
+ uint32_t checksum;
+ struct ffs_entry entries[];
+} __attribute__ ((packed));
+
+
+#endif /* __FFS_H__ */
diff --git a/op-flasher/pflash/libflash/libffs.c b/op-flasher/pflash/libflash/libffs.c
new file mode 100644
index 0000000..db7104d
--- /dev/null
+++ b/op-flasher/pflash/libflash/libffs.c
@@ -0,0 +1,265 @@
+/*
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <ccan/endian/endian.h>
+
+#include "libffs.h"
+
+enum ffs_type {
+ ffs_type_flash,
+ ffs_type_image,
+};
+
+struct ffs_handle {
+ struct ffs_hdr hdr; /* Converted header */
+ enum ffs_type type;
+ struct flash_chip *chip;
+ uint32_t flash_offset;
+ uint32_t max_size;
+ void *cache;
+ uint32_t cached_size;
+};
+
+static uint32_t ffs_checksum(void* data, size_t size)
+{
+ uint32_t i, csum = 0;
+
+ for (i = csum = 0; i < (size/4); i++)
+ csum ^= ((uint32_t *)data)[i];
+ return csum;
+}
+
+static int ffs_check_convert_header(struct ffs_hdr *dst, struct ffs_hdr *src)
+{
+ dst->magic = be32_to_cpu(src->magic);
+ if (dst->magic != FFS_MAGIC)
+ return FFS_ERR_BAD_MAGIC;
+ dst->version = be32_to_cpu(src->version);
+ if (dst->version != FFS_VERSION_1)
+ return FFS_ERR_BAD_VERSION;
+ if (ffs_checksum(src, FFS_HDR_SIZE) != 0)
+ return FFS_ERR_BAD_CKSUM;
+ dst->size = be32_to_cpu(src->size);
+ dst->entry_size = be32_to_cpu(src->entry_size);
+ dst->entry_count = be32_to_cpu(src->entry_count);
+ dst->block_size = be32_to_cpu(src->block_size);
+ dst->block_count = be32_to_cpu(src->block_count);
+
+ return 0;
+}
+
+int ffs_open_flash(struct flash_chip *chip, uint32_t offset,
+ uint32_t max_size, struct ffs_handle **ffs)
+{
+ struct ffs_hdr hdr;
+ struct ffs_handle *f;
+ uint32_t fl_size, erase_size;
+ int rc;
+
+ if (!ffs)
+ return FLASH_ERR_PARM_ERROR;
+ *ffs = NULL;
+
+ /* Grab some info about our flash chip */
+ rc = flash_get_info(chip, NULL, &fl_size, &erase_size);
+ if (rc) {
+ FL_ERR("FFS: Error %d retrieving flash info\n", rc);
+ return rc;
+ }
+ if ((offset + max_size) < offset)
+ return FLASH_ERR_PARM_ERROR;
+ if ((offset + max_size) > fl_size)
+ return FLASH_ERR_PARM_ERROR;
+
+ /* Read flash header */
+ rc = flash_read(chip, offset, &hdr, sizeof(hdr));
+ if (rc) {
+ FL_ERR("FFS: Error %d reading flash header\n", rc);
+ return rc;
+ }
+
+ /* Allocate ffs_handle structure and start populating */
+ f = malloc(sizeof(*f));
+ if (!f)
+ return FLASH_ERR_MALLOC_FAILED;
+ memset(f, 0, sizeof(*f));
+ f->type = ffs_type_flash;
+ f->flash_offset = offset;
+ f->max_size = max_size ? max_size : (fl_size - offset);
+ f->chip = chip;
+
+ /* Convert and check flash header */
+ rc = ffs_check_convert_header(&f->hdr, &hdr);
+ if (rc) {
+ FL_ERR("FFS: Error %d checking flash header\n", rc);
+ free(f);
+ return rc;
+ }
+
+ /*
+ * Decide how much of the image to grab to get the whole
+ * partition map.
+ */
+ f->cached_size = f->hdr.block_size * f->hdr.size;
+ FL_DBG("FFS: Partition map size: 0x%x\n", f->cached_size);
+
+ /* Align to erase size */
+ f->cached_size |= (erase_size - 1);
+ f->cached_size &= ~(erase_size - 1);
+ FL_DBG("FFS: Aligned to: 0x%x\n", f->cached_size);
+
+ /* Allocate cache */
+ f->cache = malloc(f->cached_size);
+ if (!f->cache) {
+ free(f);
+ return FLASH_ERR_MALLOC_FAILED;
+ }
+
+ /* Read the cached map */
+ rc = flash_read(chip, offset, f->cache, f->cached_size);
+ if (rc) {
+ FL_ERR("FFS: Error %d reading flash partition map\n", rc);
+ free(f);
+ }
+ if (rc == 0)
+ *ffs = f;
+ return rc;
+}
+
+#if 0 /* XXX TODO: For FW updates so we can copy nvram around */
+int ffs_open_image(void *image, uint32_t size, uint32_t offset,
+ struct ffs_handle **ffs)
+{
+}
+#endif
+
+void ffs_close(struct ffs_handle *ffs)
+{
+ if (ffs->cache)
+ free(ffs->cache);
+ free(ffs);
+}
+
+static struct ffs_entry *ffs_get_part(struct ffs_handle *ffs, uint32_t index,
+ uint32_t *out_offset)
+{
+ uint32_t esize = ffs->hdr.entry_size;
+ uint32_t offset = FFS_HDR_SIZE + index * esize;
+
+ if (index > ffs->hdr.entry_count)
+ return NULL;
+ if (out_offset)
+ *out_offset = offset;
+ return (struct ffs_entry *)(ffs->cache + offset);
+}
+
+static int ffs_check_convert_entry(struct ffs_entry *dst, struct ffs_entry *src)
+{
+ if (ffs_checksum(src, FFS_ENTRY_SIZE) != 0)
+ return FFS_ERR_BAD_CKSUM;
+ memcpy(dst->name, src->name, sizeof(dst->name));
+ dst->base = be32_to_cpu(src->base);
+ dst->size = be32_to_cpu(src->size);
+ dst->pid = be32_to_cpu(src->pid);
+ dst->id = be32_to_cpu(src->id);
+ dst->type = be32_to_cpu(src->type);
+ dst->flags = be32_to_cpu(src->flags);
+ dst->actual = be32_to_cpu(src->actual);
+
+ return 0;
+}
+
+int ffs_lookup_part(struct ffs_handle *ffs, const char *name,
+ uint32_t *part_idx)
+{
+ struct ffs_entry ent;
+ uint32_t i;
+ int rc;
+
+ /* Lookup the requested partition */
+ for (i = 0; i < ffs->hdr.entry_count; i++) {
+ struct ffs_entry *src_ent = ffs_get_part(ffs, i, NULL);
+ rc = ffs_check_convert_entry(&ent, src_ent);
+ if (rc) {
+ FL_ERR("FFS: Bad entry %d in partition map\n", i);
+ continue;
+ }
+ if (!strncmp(name, ent.name, sizeof(ent.name)))
+ break;
+ }
+ if (i >= ffs->hdr.entry_count)
+ return FFS_ERR_PART_NOT_FOUND;
+ if (part_idx)
+ *part_idx = i;
+ return 0;
+}
+
+int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx,
+ char **name, uint32_t *start,
+ uint32_t *total_size, uint32_t *act_size)
+{
+ struct ffs_entry *raw_ent;
+ struct ffs_entry ent;
+ char *n;
+ int rc;
+
+ if (part_idx >= ffs->hdr.entry_count)
+ return FFS_ERR_PART_NOT_FOUND;
+
+ raw_ent = ffs_get_part(ffs, part_idx, NULL);
+ if (!raw_ent)
+ return FFS_ERR_PART_NOT_FOUND;
+
+ rc = ffs_check_convert_entry(&ent, raw_ent);
+ if (rc) {
+ FL_ERR("FFS: Bad entry %d in partition map\n", part_idx);
+ return rc;
+ }
+ if (start)
+ *start = ent.base * ffs->hdr.block_size;
+ if (total_size)
+ *total_size = ent.size * ffs->hdr.block_size;
+ if (act_size)
+ *act_size = ent.actual;
+ if (name) {
+ n = malloc(PART_NAME_MAX + 1);
+ memset(n, 0, PART_NAME_MAX + 1);
+ strncpy(n, ent.name, PART_NAME_MAX);
+ *name = n;
+ }
+ return 0;
+}
+
+int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx,
+ uint32_t act_size)
+{
+ struct ffs_entry *ent;
+ uint32_t offset;
+
+ if (part_idx >= ffs->hdr.entry_count) {
+ FL_DBG("FFS: Entry out of bound\n");
+ return FFS_ERR_PART_NOT_FOUND;
+ }
+
+ ent = ffs_get_part(ffs, part_idx, &offset);
+ if (!ent) {
+ FL_DBG("FFS: Entry not found\n");
+ return FFS_ERR_PART_NOT_FOUND;
+ }
+ FL_DBG("FFS: part index %d at offset 0x%08x\n",
+ part_idx, offset);
+
+ if (ent->actual == cpu_to_be32(act_size)) {
+ FL_DBG("FFS: ent->actual alrady matches: 0x%08x==0x%08x\n",
+ cpu_to_be32(act_size), ent->actual);
+ return 0;
+ }
+ ent->actual = cpu_to_be32(act_size);
+ ent->checksum = ffs_checksum(ent, FFS_ENTRY_SIZE_CSUM);
+ if (!ffs->chip)
+ return 0;
+ return flash_smart_write(ffs->chip, offset, ent, FFS_ENTRY_SIZE);
+}
diff --git a/op-flasher/pflash/libflash/libffs.h b/op-flasher/pflash/libflash/libffs.h
new file mode 100644
index 0000000..9853987
--- /dev/null
+++ b/op-flasher/pflash/libflash/libffs.h
@@ -0,0 +1,41 @@
+#ifndef __LIBFFS_H
+#define __LIBFFS_H
+
+#include <libflash/libflash.h>
+#include <libflash/ffs.h>
+
+/* FFS handle, opaque */
+struct ffs_handle;
+
+/* Error codes:
+ *
+ * < 0 = flash controller errors
+ * 0 = success
+ * > 0 = libffs / libflash errors
+ */
+#define FFS_ERR_BAD_MAGIC 100
+#define FFS_ERR_BAD_VERSION 101
+#define FFS_ERR_BAD_CKSUM 102
+#define FFS_ERR_PART_NOT_FOUND 103
+
+int ffs_open_flash(struct flash_chip *chip, uint32_t offset,
+ uint32_t max_size, struct ffs_handle **ffs);
+
+/* TODO
+int ffs_open_image(void *image, uint32_t size, struct ffs_handle **ffs);
+*/
+
+void ffs_close(struct ffs_handle *ffs);
+
+int ffs_lookup_part(struct ffs_handle *ffs, const char *name,
+ uint32_t *part_idx);
+
+int ffs_part_info(struct ffs_handle *ffs, uint32_t part_idx,
+ char **name, uint32_t *start,
+ uint32_t *total_size, uint32_t *act_size);
+
+int ffs_update_act_size(struct ffs_handle *ffs, uint32_t part_idx,
+ uint32_t act_size);
+
+
+#endif /* __LIBFFS_H */
diff --git a/op-flasher/pflash/libflash/libflash-priv.h b/op-flasher/pflash/libflash/libflash-priv.h
new file mode 100644
index 0000000..cf40e2e
--- /dev/null
+++ b/op-flasher/pflash/libflash/libflash-priv.h
@@ -0,0 +1,217 @@
+#ifndef __LIBFLASH_PRIV_H
+#define __LIBFLASH_PRIV_H
+
+#include <ccan/endian/endian.h>
+#include <ccan/array_size/array_size.h>
+#include <ccan/container_of/container_of.h>
+
+/* Flash commands */
+#define CMD_WRSR 0x01 /* Write Status Register (also config. on Macronix) */
+#define CMD_PP 0x02 /* Page Program */
+#define CMD_READ 0x03 /* READ */
+#define CMD_WRDI 0x04 /* Write Disable */
+#define CMD_RDSR 0x05 /* Read Status Register */
+#define CMD_WREN 0x06 /* Write Enable */
+#define CMD_RDCR 0x15 /* Read configuration register (Macronix) */
+#define CMD_SE 0x20 /* Sector (4K) Erase */
+#define CMD_RDSCUR 0x2b /* Read Security Register (Macronix) */
+#define CMD_BE32K 0x52 /* Block (32K) Erase */
+#define CMD_RDSFDP 0x5a /* Read SFDP JEDEC info */
+#define CMD_CE 0x60 /* Chip Erase (Macronix/Winbond) */
+#define CMD_MIC_WREVCONF 0x61 /* Micron Write Enhanced Volatile Config */
+#define CMD_MIC_RDEVCONF 0x65 /* Micron Read Enhanced Volatile Config */
+#define CMD_MIC_RDFLST 0x70 /* Micron Read Flag Status */
+#define CMD_MIC_WRVCONF 0x81 /* Micron Write Volatile Config */
+#define CMD_MIC_RDVCONF 0x85 /* Micron Read Volatile Config */
+#define CMD_RDID 0x9f /* Read JEDEC ID */
+#define CMD_EN4B 0xb7 /* Enable 4B addresses */
+#define CMD_MIC_BULK_ERASE 0xc7 /* Micron Bulk Erase */
+#define CMD_BE 0xd8 /* Block (64K) Erase */
+#define CMD_RDDPB 0xe0 /* Read dynamic protection (Macronix) */
+#define CMD_RDSPB 0xe2 /* Read static protection (Macronix) */
+#define CMD_EX4B 0xe9 /* Exit 4B addresses */
+
+/* Flash status bits */
+#define STAT_WIP 0x01
+#define STAT_WEN 0x02
+
+/* This isn't exposed to clients but is to controllers */
+struct flash_info {
+ uint32_t id;
+ uint32_t size;
+ uint32_t flags;
+#define FL_ERASE_4K 0x00000001 /* Supports 4k erase */
+#define FL_ERASE_32K 0x00000002 /* Supports 32k erase */
+#define FL_ERASE_64K 0x00000004 /* Supports 64k erase */
+#define FL_ERASE_CHIP 0x00000008 /* Supports 0x60 cmd chip erase */
+#define FL_ERASE_BULK 0x00000010 /* Supports 0xc7 cmd bulk erase */
+#define FL_MICRON_BUGS 0x00000020 /* Various micron bug workarounds */
+#define FL_ERASE_ALL (FL_ERASE_4K | FL_ERASE_32K | FL_ERASE_64K | \
+ FL_ERASE_CHIP)
+#define FL_CAN_4B 0x00000010 /* Supports 4b mode */
+ const char *name;
+};
+
+/* Flash controller, return negative values for errors */
+struct spi_flash_ctrl {
+ /*
+ * The controller can provide basically two interfaces,
+ * either a fairly high level one and a lower level one.
+ *
+ * If all functions of the high level interface are
+ * implemented then the low level one is optional. A
+ * controller can implement some of the high level one
+ * in which case the missing ones will be handled by
+ * libflash using the low level interface.
+ *
+ * There are also some common functions.
+ */
+
+ /* **************************************************
+ * Misc / common functions
+ * **************************************************/
+
+ /*
+ * - setup(ctrl, tsize)
+ *
+ * Provides the controller with an option to configure itself
+ * based on the specific flash type. It can also override some
+ * settings in the info block such as available erase sizes etc...
+ * which can be needed for high level controllers. It can also
+ * override the total flash size.
+ */
+ int (*setup)(struct spi_flash_ctrl *ctrl, uint32_t *tsize);
+
+ /*
+ * - set_4b(ctrl, enable)
+ *
+ * enable : Switch to 4bytes (true) or 3bytes (false) address mode
+ *
+ * Set the controller's address size. If the controller doesn't
+ * implement the low level command interface, then this must also
+ * configure the flash chip itself. Otherwise, libflash will do it.
+ *
+ * Note that if this isn't implemented, then libflash might still
+ * try to switch large flash chips to 4b mode if the low level cmd
+ * interface is implemented. It will then also stop using the high
+ * level command interface since it's assumed that it cannot handle
+ * 4b addresses.
+ */
+ int (*set_4b)(struct spi_flash_ctrl *ctrl, bool enable);
+
+
+
+ /* **************************************************
+ * High level interface
+ * **************************************************/
+
+ /*
+ * Read chip ID. This can return up to 16 bytes though the
+ * current libflash will only use 3 (room for things like
+ * extended micron stuff).
+ *
+ * id_size is set on entry to the buffer size and need to
+ * be adjusted to the actual ID size read.
+ *
+ * If NULL, libflash will use cmd_rd to send normal RDID (0x9f)
+ * command.
+ */
+ int (*chip_id)(struct spi_flash_ctrl *ctrl, uint8_t *id_buf,
+ uint32_t *id_size);
+
+ /*
+ * Read from flash. There is no specific constraint on
+ * alignment or size other than not reading outside of
+ * the chip.
+ *
+ * If NULL, libflash will use cmd_rd to send normal
+ * READ (0x03) commands.
+ */
+ int (*read)(struct spi_flash_ctrl *ctrl, uint32_t addr, void *buf,
+ uint32_t size);
+
+ /*
+ * Write to flash. There is no specific constraint on
+ * alignment or size other than not reading outside of
+ * the chip. The driver is responsible for handling
+ * 256-bytes page alignment and to send the write enable
+ * commands when needed.
+ *
+ * If absent, libflash will use cmd_wr to send WREN (0x06)
+ * and PP (0x02) commands.
+ *
+ * Note: This does not need to handle erasing. libflash
+ * will ensure that this is never used for changing a bit
+ * value from 0 to 1.
+ */
+ int (*write)(struct spi_flash_ctrl *ctrl, uint32_t addr,
+ const void *buf, uint32_t size);
+
+ /*
+ * Erase. This will be called for erasing a portion of
+ * the flash using a granularity (alignment of start and
+ * size) that is no less than the smallest supported
+ * erase size in the info block (*). The driver is
+ * responsible to send write enable commands when needed.
+ *
+ * If absent, libflash will use cmd_wr to send WREN (0x06)
+ * and either of SE (0x20), BE32K (0x52) or BE (0xd8)
+ * based on what the flash chip supports.
+ *
+ * (*) Note: This is called with addr=0 and size=0xffffffff
+ * in which case this is used as a "chip erase". Return
+ * FLASH_ERR_CHIP_ER_NOT_SUPPORTED if not supported. Some
+ * future version of libflash might then emulate it using
+ * normal erase commands.
+ */
+ int (*erase)(struct spi_flash_ctrl *ctrl, uint32_t addr,
+ uint32_t size);
+
+ /* **************************************************
+ * Low level interface
+ * **************************************************/
+
+ /* Note: For commands with no data, libflash will might use
+ * either cmd_rd or cmd_wr.
+ */
+
+ /*
+ * - cmd_rd(ctrl, cmd, has_addr, address, buffer, size);
+ *
+ * cmd : command opcode
+ * has_addr : send an address after the command
+ * address : address to send
+ * buffer : buffer for additional data to read (or NULL)
+ * size : size of additional data read (or NULL)
+ *
+ * Sends a command and optionally read additional data
+ */
+ int (*cmd_rd)(struct spi_flash_ctrl *ctrl, uint8_t cmd,
+ bool has_addr, uint32_t addr, void *buffer,
+ uint32_t size);
+ /*
+ * - cmd_wr(ctrl, cmd, has_addr, address, buffer, size);
+ *
+ * cmd : command opcode
+ * has_addr : send an address after the command
+ * address : address to send
+ * buffer : buffer for additional data to write (or NULL)
+ * size : size of additional data write (or NULL)
+ *
+ * Sends a command and optionally write additional data
+ */
+ int (*cmd_wr)(struct spi_flash_ctrl *ctrl, uint8_t cmd,
+ bool has_addr, uint32_t addr, const void *buffer,
+ uint32_t size);
+
+ /* The core will establish this at init, after chip ID has
+ * been probed
+ */
+ struct flash_info *finfo;
+};
+
+extern int fl_wren(struct spi_flash_ctrl *ct);
+extern int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat);
+extern int fl_sync_wait_idle(struct spi_flash_ctrl *ct);
+
+#endif /* LIBFLASH_PRIV_H */
diff --git a/op-flasher/pflash/libflash/libflash.c b/op-flasher/pflash/libflash/libflash.c
new file mode 100644
index 0000000..762d9e7
--- /dev/null
+++ b/op-flasher/pflash/libflash/libflash.c
@@ -0,0 +1,691 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libflash.h"
+#include "libflash-priv.h"
+
+static const struct flash_info flash_info[] = {
+ { 0xc22019, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL25635F"},
+ { 0xc2201a, 0x04000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL51235F"},
+ { 0xef4018, 0x01000000, FL_ERASE_ALL, "Winbond W25Q128BV" },
+ { 0xef4019, 0x02000000, FL_ERASE_ALL | FL_CAN_4B | FL_ERASE_64K|FL_ERASE_BULK, "Winbond W25Q256BV"},
+ { 0x20ba20, 0x04000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B |
+ FL_ERASE_BULK | FL_MICRON_BUGS,
+ "Micron N25Qx512Ax" },
+ { 0x20ba19, 0x02000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B |
+ FL_ERASE_BULK | FL_MICRON_BUGS,
+ "Micron N25Q256Ax" },
+ { 0x1940ef, 0x02000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B |
+ FL_ERASE_BULK | FL_MICRON_BUGS,
+ "Micron N25Qx256Ax" },
+ { 0x55aa55, 0x00100000, FL_ERASE_ALL | FL_CAN_4B, "TEST_FLASH" },
+
+};
+
+struct flash_chip {
+ struct spi_flash_ctrl *ctrl; /* Controller */
+ struct flash_info info; /* Flash info */
+ uint32_t tsize; /* Corrected flash size */
+ uint32_t min_erase_mask; /* Minimum erase size */
+ bool mode_4b; /* Flash currently in 4b mode */
+ struct flash_req *cur_req; /* Current request */
+ void *smart_buf; /* Buffer for smart writes */
+};
+
+bool libflash_debug;
+
+int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat)
+{
+ return ct->cmd_rd(ct, CMD_RDSR, false, 0, stat, 1);
+}
+
+static void fl_micron_status(struct spi_flash_ctrl *ct)
+{
+ uint8_t flst;
+
+ /*
+ * After a success status on a write or erase, we
+ * need to do that command or some chip variants will
+ * lock
+ */
+ ct->cmd_rd(ct, CMD_MIC_RDFLST, false, 0, &flst, 1);
+}
+
+/* Synchronous write completion, probably need a yield hook */
+int fl_sync_wait_idle(struct spi_flash_ctrl *ct)
+{
+ uint8_t stat;
+ int rc;
+
+ /* XXX Add timeout */
+ for (;;) {
+ rc = fl_read_stat(ct, &stat);
+ if (rc) return rc;
+ if (!(stat & STAT_WIP)) {
+ if (ct->finfo->flags & FL_MICRON_BUGS)
+ fl_micron_status(ct);
+ return 0;
+ }
+ }
+ /* return FLASH_ERR_WIP_TIMEOUT; */
+}
+
+/* Exported for internal use */
+int fl_wren(struct spi_flash_ctrl *ct)
+{
+ int i, rc;
+ uint8_t stat;
+
+ /* Some flashes need it to be hammered */
+ for (i = 0; i < 1000; i++) {
+ rc = ct->cmd_wr(ct, CMD_WREN, false, 0, NULL, 0);
+ if (rc) return rc;
+ rc = fl_read_stat(ct, &stat);
+ if (rc) return rc;
+ if (stat & STAT_WIP) {
+ FL_ERR("LIBFLASH: WREN has WIP status set !\n");
+ rc = fl_sync_wait_idle(ct);
+ if (rc)
+ return rc;
+ continue;
+ }
+ if (stat & STAT_WEN)
+ return 0;
+ }
+ return FLASH_ERR_WREN_TIMEOUT;
+}
+
+int flash_read(struct flash_chip *c, uint32_t pos, void *buf, uint32_t len)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+
+ /* XXX Add sanity/bound checking */
+
+ /*
+ * If the controller supports read and either we are in 3b mode
+ * or we are in 4b *and* the controller supports it, then do a
+ * high level read.
+ */
+ if ((!c->mode_4b || ct->set_4b) && ct->read)
+ return ct->read(ct, pos, buf, len);
+
+ /* Otherwise, go manual if supported */
+ if (!ct->cmd_rd)
+ return FLASH_ERR_CTRL_CMD_UNSUPPORTED;
+ return ct->cmd_rd(ct, CMD_READ, true, pos, buf, len);
+}
+
+static void fl_get_best_erase(struct flash_chip *c, uint32_t dst, uint32_t size,
+ uint32_t *chunk, uint8_t *cmd)
+{
+ /* Smaller than 32k, use 4k */
+ if ((dst & 0x7fff) || (size < 0x8000)) {
+ *chunk = 0x1000;
+ *cmd = CMD_SE;
+ return;
+ }
+ /* Smaller than 64k and 32k is supported, use it */
+ if ((c->info.flags & FL_ERASE_32K) &&
+ ((dst & 0xffff) || (size < 0x10000))) {
+ *chunk = 0x8000;
+ *cmd = CMD_BE32K;
+ return;
+ }
+ /* If 64K is not supported, use whatever smaller size is */
+ if (!(c->info.flags & FL_ERASE_64K)) {
+ if (c->info.flags & FL_ERASE_32K) {
+ *chunk = 0x8000;
+ *cmd = CMD_BE32K;
+ } else {
+ *chunk = 0x1000;
+ *cmd = CMD_SE;
+ }
+ return;
+ }
+ /* Allright, let's go for 64K */
+ *chunk = 0x10000;
+ *cmd = CMD_BE;
+}
+
+int flash_erase(struct flash_chip *c, uint32_t dst, uint32_t size)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ uint32_t chunk;
+ uint8_t cmd;
+ int rc;
+
+ /* Some sanity checking */
+ if (((dst + size) <= dst) || !size || (dst + size) > c->tsize)
+ return FLASH_ERR_PARM_ERROR;
+
+ /* Check boundaries fit erase blocks */
+ if ((dst | size) & c->min_erase_mask)
+ return FLASH_ERR_ERASE_BOUNDARY;
+
+ FL_DBG("LIBFLASH: Erasing 0x%08x..0%08x...\n", dst, dst + size);
+
+ /* Use controller erase if supported */
+ if (ct->erase)
+ return ct->erase(ct, dst, size);
+
+ /* Allright, loop as long as there's something to erase */
+ while(size) {
+ /* How big can we make it based on alignent & size */
+ fl_get_best_erase(c, dst, size, &chunk, &cmd);
+
+ /* Poke write enable */
+ rc = fl_wren(ct);
+ if (rc)
+ return rc;
+
+ /* Send erase command */
+ rc = ct->cmd_wr(ct, cmd, true, dst, NULL, 0);
+ if (rc)
+ return rc;
+
+ /* Wait for write complete */
+ rc = fl_sync_wait_idle(ct);
+ if (rc)
+ return rc;
+
+ size -= chunk;
+ dst += chunk;
+ }
+ return 0;
+}
+
+int flash_erase_chip(struct flash_chip *c)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ int rc;
+
+ /* XXX TODO: Fallback to using normal erases */
+ if (!(c->info.flags & (FL_ERASE_CHIP|FL_ERASE_BULK)))
+ return FLASH_ERR_CHIP_ER_NOT_SUPPORTED;
+
+ FL_DBG("LIBFLASH: Erasing chip...\n");
+
+ /* Use controller erase if supported */
+ if (ct->erase)
+ return ct->erase(ct, 0, 0xffffffff);
+
+ rc = fl_wren(ct);
+ if (rc) return rc;
+
+ if (c->info.flags & FL_ERASE_CHIP)
+ rc = ct->cmd_wr(ct, CMD_CE, false, 0, NULL, 0);
+ else
+ rc = ct->cmd_wr(ct, CMD_MIC_BULK_ERASE, false, 0, NULL, 0);
+ if (rc)
+ return rc;
+
+ /* Wait for write complete */
+ return fl_sync_wait_idle(ct);
+}
+
+static int fl_wpage(struct flash_chip *c, uint32_t dst, const void *src,
+ uint32_t size)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ int rc;
+
+ if (size < 1 || size > 0x100)
+ return FLASH_ERR_BAD_PAGE_SIZE;
+
+ rc = fl_wren(ct);
+ if (rc) return rc;
+
+ rc = ct->cmd_wr(ct, CMD_PP, true, dst, src, size);
+ if (rc)
+ return rc;
+
+ /* Wait for write complete */
+ return fl_sync_wait_idle(ct);
+}
+
+int flash_write(struct flash_chip *c, uint32_t dst, const void *src,
+ uint32_t size, bool verify)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ uint32_t todo = size;
+ uint32_t d = dst;
+ const void *s = src;
+ uint8_t vbuf[0x100];
+ int rc;
+
+ /* Some sanity checking */
+ if (((dst + size) <= dst) || !size || (dst + size) > c->tsize)
+ return FLASH_ERR_PARM_ERROR;
+
+ FL_DBG("LIBFLASH: Writing to 0x%08x..0%08x...\n", dst, dst + size);
+
+ /*
+ * If the controller supports write and either we are in 3b mode
+ * or we are in 4b *and* the controller supports it, then do a
+ * high level write.
+ */
+ if ((!c->mode_4b || ct->set_4b) && ct->write) {
+ rc = ct->write(ct, dst, src, size);
+ if (rc)
+ return rc;
+ goto writing_done;
+ }
+
+ /* Otherwise, go manual if supported */
+ if (!ct->cmd_wr)
+ return FLASH_ERR_CTRL_CMD_UNSUPPORTED;
+
+ /* Iterate for each page to write */
+ while(todo) {
+ uint32_t chunk;
+
+ /* Handle misaligned start */
+ chunk = 0x100 - (d & 0xff);
+ if (chunk > 0x100)
+ chunk = 0x100;
+ if (chunk > todo)
+ chunk = todo;
+
+ rc = fl_wpage(c, d, s, chunk);
+ if (rc) return rc;
+ d += chunk;
+ s += chunk;
+ todo -= chunk;
+ }
+
+ writing_done:
+ if (!verify)
+ return 0;
+
+ /* Verify */
+ FL_DBG("LIBFLASH: Verifying...\n");
+
+ while(size) {
+ uint32_t chunk;
+
+ chunk = sizeof(vbuf);
+ if (chunk > size)
+ chunk = size;
+ rc = flash_read(c, dst, vbuf, chunk);
+ if (rc) return rc;
+ if (memcmp(vbuf, src, chunk)) {
+ FL_ERR("LIBFLASH: Miscompare at 0x%08x\n", dst);
+ return FLASH_ERR_VERIFY_FAILURE;
+ }
+ dst += chunk;
+ src += chunk;
+ size -= chunk;
+ }
+ return 0;
+}
+
+enum sm_comp_res {
+ sm_no_change,
+ sm_need_write,
+ sm_need_erase,
+};
+
+static enum sm_comp_res flash_smart_comp(struct flash_chip *c,
+ const void *src,
+ uint32_t offset, uint32_t size)
+{
+ uint8_t *b = c->smart_buf + offset;
+ const uint8_t *s = src;
+ bool is_same = true;
+ uint32_t i;
+
+ /* SRC DEST NEED_ERASE
+ * 0 1 0
+ * 1 1 0
+ * 0 0 0
+ * 1 0 1
+ */
+ for (i = 0; i < size; i++) {
+ /* Any bit need to be set, need erase */
+ if (s[i] & ~b[i])
+ return sm_need_erase;
+ if (is_same && (b[i] != s[i]))
+ is_same = false;
+ }
+ return is_same ? sm_no_change : sm_need_write;
+}
+
+int flash_smart_write(struct flash_chip *c, uint32_t dst, const void *src,
+ uint32_t size)
+{
+ uint32_t er_size = c->min_erase_mask + 1;
+ uint32_t end = dst + size;
+ int rc;
+
+ /* Some sanity checking */
+ if (end <= dst || !size || end > c->tsize) {
+ FL_DBG("LIBFLASH: Smart write param error\n");
+ return FLASH_ERR_PARM_ERROR;
+ }
+
+ FL_DBG("LIBFLASH: Smart writing to 0x%08x..0%08x...\n",
+ dst, dst + size);
+
+ /* As long as we have something to write ... */
+ while(dst < end) {
+ uint32_t page, off, chunk;
+ enum sm_comp_res sr;
+
+ /* Figure out which erase page we are in and read it */
+ page = dst & ~c->min_erase_mask;
+ off = dst & c->min_erase_mask;
+ FL_DBG("LIBFLASH: reading page 0x%08x..0x%08x...",
+ page, page + er_size);
+ rc = flash_read(c, page, c->smart_buf, er_size);
+ if (rc) {
+ FL_DBG(" error %d!\n", rc);
+ return rc;
+ }
+
+ /* Locate the chunk of data we are working on */
+ chunk = er_size - off;
+ if (size < chunk)
+ chunk = size;
+
+ /* Compare against what we are writing and ff */
+ sr = flash_smart_comp(c, src, off, chunk);
+ switch(sr) {
+ case sm_no_change:
+ /* Identical, skip it */
+ FL_DBG(" same !\n");
+ break;
+ case sm_need_write:
+ /* Just needs writing over */
+ FL_DBG(" need write !\n");
+ rc = flash_write(c, dst, src, chunk, true);
+ if (rc) {
+ FL_DBG("LIBFLASH: Write error %d !\n", rc);
+ return rc;
+ }
+ break;
+ case sm_need_erase:
+ FL_DBG(" need erase !\n");
+ rc = flash_erase(c, page, er_size);
+ if (rc) {
+ FL_DBG("LIBFLASH: erase error %d !\n", rc);
+ return rc;
+ }
+ /* Then update the portion of the buffer and write the block */
+ memcpy(c->smart_buf + off, src, chunk);
+ rc = flash_write(c, page, c->smart_buf, er_size, true);
+ if (rc) {
+ FL_DBG("LIBFLASH: write error %d !\n", rc);
+ return rc;
+ }
+ break;
+ }
+ dst += chunk;
+ src += chunk;
+ size -= chunk;
+ }
+ return 0;
+}
+
+static int fl_chip_id(struct spi_flash_ctrl *ct, uint8_t *id_buf,
+ uint32_t *id_size)
+{
+ int rc;
+ uint8_t stat;
+
+ /* Check initial status */
+ rc = fl_read_stat(ct, &stat);
+ if (rc)
+ return rc;
+
+ /* If stuck writing, wait for idle */
+ if (stat & STAT_WIP) {
+ FL_ERR("LIBFLASH: Flash in writing state ! Waiting...\n");
+ rc = fl_sync_wait_idle(ct);
+ if (rc)
+ return rc;
+ } else
+ FL_DBG("LIBFLASH: Init status: %02x\n", stat);
+
+ /* Fallback to get ID manually */
+ rc = ct->cmd_rd(ct, CMD_RDID, false, 0, id_buf, 3);
+ if (rc)
+ return rc;
+ *id_size = 3;
+
+ return 0;
+}
+
+static int flash_identify(struct flash_chip *c)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ const struct flash_info *info;
+ uint32_t iid, id_size;
+#define MAX_ID_SIZE 16
+ uint8_t id[MAX_ID_SIZE];
+ int rc, i;
+
+ if (ct->chip_id) {
+ /* High level controller interface */
+ id_size = MAX_ID_SIZE;
+ rc = ct->chip_id(ct, id, &id_size);
+ } else
+ rc = fl_chip_id(ct, id, &id_size);
+ if (rc)
+ return rc;
+ if (id_size < 3)
+ return FLASH_ERR_CHIP_UNKNOWN;
+
+ /* Convert to a dword for lookup */
+ iid = id[0];
+ iid = (iid << 8) | id[1];
+ iid = (iid << 8) | id[2];
+
+ FL_DBG("LIBFLASH: Flash ID: %02x.%02x.%02x (%06x)\n",
+ id[0], id[1], id[2], iid);
+
+ /* Lookup in flash_info */
+ for (i = 0; i < ARRAY_SIZE(flash_info); i++) {
+ info = &flash_info[i];
+ if (info->id == iid)
+ break;
+ }
+ if (info->id != iid)
+ return FLASH_ERR_CHIP_UNKNOWN;
+
+ c->info = *info;
+ c->tsize = info->size;
+ ct->finfo = &c->info;
+
+ /*
+ * Let controller know about our settings and possibly
+ * override them
+ */
+ if (ct->setup) {
+ rc = ct->setup(ct, &c->tsize);
+ if (rc)
+ return rc;
+ }
+
+ /* Calculate min erase granularity */
+ if (c->info.flags & FL_ERASE_4K)
+ c->min_erase_mask = 0xfff;
+ else if (c->info.flags & FL_ERASE_32K)
+ c->min_erase_mask = 0x7fff;
+ else if (c->info.flags & FL_ERASE_64K)
+ c->min_erase_mask = 0xffff;
+ else {
+ /* No erase size ? oops ... */
+ FL_ERR("LIBFLASH: No erase sizes !\n");
+ return FLASH_ERR_CTRL_CONFIG_MISMATCH;
+ }
+
+ FL_DBG("LIBFLASH: Found chip %s size %dM erase granule: %dK\n",
+ c->info.name, c->tsize >> 20, (c->min_erase_mask + 1) >> 10);
+
+ return 0;
+}
+
+static int flash_set_4b(struct flash_chip *c, bool enable)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ int rc;
+
+ /* Some flash chips want this */
+ rc = fl_wren(ct);
+ if (rc) {
+ FL_ERR("LIBFLASH: Error %d enabling write for set_4b\n", rc);
+ /* Ignore the error & move on (could be wrprotect chip) */
+ }
+
+ /* Ignore error in case chip is write protected */
+
+ return ct->cmd_wr(ct, enable ? CMD_EN4B : CMD_EX4B, false, 0, NULL, 0);
+}
+
+int flash_force_4b_mode(struct flash_chip *c, bool enable_4b)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ int rc;
+
+ /*
+ * We only allow force 4b if both controller and flash do 4b
+ * as this is mainly used if a 3rd party tries to directly
+ * access a direct mapped read region
+ */
+ if (enable_4b && !((c->info.flags & FL_CAN_4B) && ct->set_4b))
+ return FLASH_ERR_4B_NOT_SUPPORTED;
+
+ /* Only send to flash directly on controllers that implement
+ * the low level callbacks
+ */
+ if (ct->cmd_wr) {
+ rc = flash_set_4b(c, enable_4b);
+ if (rc)
+ return rc;
+ }
+
+ /* Then inform the controller */
+ if (ct->set_4b)
+ rc = ct->set_4b(ct, enable_4b);
+ return rc;
+}
+
+static int flash_configure(struct flash_chip *c)
+{
+ struct spi_flash_ctrl *ct = c->ctrl;
+ int rc;
+
+ /* Crop flash size if necessary */
+ if (c->tsize > 0x01000000 && !(c->info.flags & FL_CAN_4B)) {
+ FL_ERR("LIBFLASH: Flash chip cropped to 16M, no 4b mode\n");
+ c->tsize = 0x01000000;
+ }
+
+ /* If flash chip > 16M, enable 4b mode */
+ if (c->tsize > 0x01000000) {
+ FL_DBG("LIBFLASH: Flash >16MB, enabling 4B mode...\n");
+
+ /* Set flash to 4b mode if we can */
+ if (ct->cmd_wr) {
+ rc = flash_set_4b(c, true);
+ if (rc) {
+ FL_ERR("LIBFLASH: Failed to set flash 4b mode\n");
+ return rc;
+ }
+ }
+
+
+ /* Set controller to 4b mode if supported */
+ if (ct->set_4b) {
+ FL_DBG("LIBFLASH: Enabling controller 4B mode...\n");
+ rc = ct->set_4b(ct, true);
+ if (rc) {
+ FL_ERR("LIBFLASH: Failed"
+ " to set controller 4b mode\n");
+ return rc;
+ }
+ }
+ } else {
+ FL_DBG("LIBFLASH: Flash <=16MB, disabling 4B mode...\n");
+
+ /*
+ * If flash chip supports 4b mode, make sure we disable
+ * it in case it was left over by the previous user
+ */
+ if (c->info.flags & FL_CAN_4B) {
+ rc = flash_set_4b(c, false);
+ if (rc) {
+ FL_ERR("LIBFLASH: Failed to"
+ " clear flash 4b mode\n");
+ return rc;
+ }
+ }
+
+ /* Set controller to 3b mode if mode switch is supported */
+ if (ct->set_4b) {
+ FL_DBG("LIBFLASH: Disabling controller 4B mode...\n");
+ rc = ct->set_4b(ct, false);
+ if (rc) {
+ FL_ERR("LIBFLASH: Failed to"
+ " clear controller 4b mode\n");
+ return rc;
+ }
+ }
+ }
+ return 0;
+}
+
+int flash_get_info(struct flash_chip *chip, const char **name,
+ uint32_t *total_size, uint32_t *erase_granule)
+{
+ if (name)
+ *name = chip->info.name;
+ if (total_size)
+ *total_size = chip->tsize;
+ if (erase_granule)
+ *erase_granule = chip->min_erase_mask + 1;
+ return 0;
+}
+
+int flash_init(struct spi_flash_ctrl *ctrl, struct flash_chip **flash)
+{
+ struct flash_chip *c;
+ int rc;
+
+ *flash = NULL;
+ c = malloc(sizeof(struct flash_chip));
+ if (!c)
+ return FLASH_ERR_MALLOC_FAILED;
+ memset(c, 0, sizeof(*c));
+ c->ctrl = ctrl;
+
+ rc = flash_identify(c);
+ if (rc) {
+ FL_ERR("LIBFLASH: Flash identification failed\n");
+ goto bail;
+ }
+ c->smart_buf = malloc(c->min_erase_mask + 1);
+ if (!c->smart_buf) {
+ FL_ERR("LIBFLASH: Failed to allocate smart buffer !\n");
+ rc = FLASH_ERR_MALLOC_FAILED;
+ goto bail;
+ }
+ rc = flash_configure(c);
+ if (rc)
+ FL_ERR("LIBFLASH: Flash configuration failed\n");
+ bail:
+ if (rc) {
+ free(c);
+ return rc;
+ }
+ *flash = c;
+ return 0;
+}
+
+void flash_exit(struct flash_chip *chip)
+{
+ /* XXX Make sure we are idle etc... */
+ free(chip);
+}
+
diff --git a/op-flasher/pflash/libflash/libflash.h b/op-flasher/pflash/libflash/libflash.h
new file mode 100644
index 0000000..a341c54
--- /dev/null
+++ b/op-flasher/pflash/libflash/libflash.h
@@ -0,0 +1,69 @@
+#ifndef __LIBFLASH_H
+#define __LIBFLASH_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef FL_INF
+#define FL_INF(fmt...) do { printf(fmt); } while(0)
+#endif
+
+#ifndef FL_DBG
+#define FL_DBG(fmt...) do { if (libflash_debug) printf(fmt); } while(0)
+#endif
+
+#ifndef FL_ERR
+#define FL_ERR(fmt...) do { printf(fmt); } while(0)
+#endif
+
+extern bool libflash_debug;
+
+/* API status/return:
+ *
+ * <0 = flash controller errors passed through,
+ * 0 = success
+ * >0 = libflash error
+ */
+
+#define FLASH_ERR_MALLOC_FAILED 1
+#define FLASH_ERR_CHIP_UNKNOWN 2
+#define FLASH_ERR_PARM_ERROR 3
+#define FLASH_ERR_ERASE_BOUNDARY 4
+#define FLASH_ERR_WREN_TIMEOUT 5
+#define FLASH_ERR_WIP_TIMEOUT 6
+#define FLASH_ERR_BAD_PAGE_SIZE 7
+#define FLASH_ERR_VERIFY_FAILURE 8
+#define FLASH_ERR_4B_NOT_SUPPORTED 9
+#define FLASH_ERR_CTRL_CONFIG_MISMATCH 10
+#define FLASH_ERR_CHIP_ER_NOT_SUPPORTED 11
+#define FLASH_ERR_CTRL_CMD_UNSUPPORTED 12
+#define FLASH_ERR_CTRL_TIMEOUT 13
+
+/* Flash chip, opaque */
+struct flash_chip;
+struct spi_flash_ctrl;
+
+int flash_init(struct spi_flash_ctrl *ctrl, struct flash_chip **flash);
+void flash_exit(struct flash_chip *chip);
+
+int flash_get_info(struct flash_chip *chip, const char **name,
+ uint32_t *total_size, uint32_t *erase_granule);
+
+/* libflash sets the 4b mode automatically based on the flash
+ * size and controller capabilities but it can be overriden
+ */
+int flash_force_4b_mode(struct flash_chip *chip, bool enable_4b);
+
+int flash_read(struct flash_chip *c, uint32_t pos, void *buf, uint32_t len);
+int flash_erase(struct flash_chip *c, uint32_t dst, uint32_t size);
+int flash_write(struct flash_chip *c, uint32_t dst, const void *src,
+ uint32_t size, bool verify);
+int flash_smart_write(struct flash_chip *c, uint32_t dst, const void *src,
+ uint32_t size);
+
+/* chip erase may not be supported by all chips/controllers, get ready
+ * for FLASH_ERR_CHIP_ER_NOT_SUPPORTED
+ */
+int flash_erase_chip(struct flash_chip *c);
+
+#endif /* __LIBFLASH_H */
diff --git a/op-flasher/pflash/libflash/test/Makefile b/op-flasher/pflash/libflash/test/Makefile
new file mode 100644
index 0000000..e357e20
--- /dev/null
+++ b/op-flasher/pflash/libflash/test/Makefile
@@ -0,0 +1,2 @@
+test_flash: test-flash.c
+ $(CC) -o $@ $^ -Wall -I../../ -include ../../config.h
diff --git a/op-flasher/pflash/libflash/test/test-flash.c b/op-flasher/pflash/libflash/test/test-flash.c
new file mode 100644
index 0000000..0e92d8c
--- /dev/null
+++ b/op-flasher/pflash/libflash/test/test-flash.c
@@ -0,0 +1,403 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "../libflash.c"
+
+#define __unused __attribute__((unused))
+
+#define ERR(fmt...) fprintf(stderr, fmt)
+
+/* Flash commands */
+#define CMD_PP 0x02
+#define CMD_READ 0x03
+#define CMD_WRDI 0x04
+#define CMD_RDSR 0x05
+#define CMD_WREN 0x06
+#define CMD_SE 0x20
+#define CMD_RDSCUR 0x2b
+#define CMD_BE32K 0x52
+#define CMD_CE 0x60
+#define CMD_RDID 0x9f
+#define CMD_EN4B 0xb7
+#define CMD_BE 0xd8
+#define CMD_RDDPB 0xe0
+#define CMD_RDSPB 0xe2
+#define CMD_EX4B 0xe9
+
+/* Flash status bits */
+#define STAT_WIP 0x01
+#define STAT_WEN 0x02
+
+static uint8_t *sim_image;
+static uint32_t sim_image_sz = 0x100000;
+static uint32_t sim_index;
+static uint32_t sim_addr;
+static uint32_t sim_er_size;
+static uint8_t sim_sr;
+static bool sim_fl_4b;
+static bool sim_ct_4b;
+
+static enum sim_state {
+ sim_state_idle,
+ sim_state_rdid,
+ sim_state_rdsr,
+ sim_state_read_addr,
+ sim_state_read_data,
+ sim_state_write_addr,
+ sim_state_write_data,
+ sim_state_erase_addr,
+ sim_state_erase_done,
+} sim_state;
+
+/*
+ * Simulated flash & controller
+ */
+static int sim_start_cmd(uint8_t cmd)
+{
+ if (sim_state != sim_state_idle) {
+ ERR("SIM: Command %02x in wrong state %d\n", cmd, sim_state);
+ return -1;
+ }
+
+ sim_index = 0;
+ sim_addr = 0;
+
+ switch(cmd) {
+ case CMD_RDID:
+ sim_state = sim_state_rdid;
+ break;
+ case CMD_RDSR:
+ sim_state = sim_state_rdsr;
+ break;
+ case CMD_EX4B:
+ sim_fl_4b = false;
+ break;
+ case CMD_EN4B:
+ sim_fl_4b = true;
+ break;
+ case CMD_WREN:
+ sim_sr |= STAT_WEN;
+ break;
+ case CMD_READ:
+ sim_state = sim_state_read_addr;
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in READ !\n");
+ break;
+ case CMD_PP:
+ sim_state = sim_state_write_addr;
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in PP !\n");
+ if (!(sim_sr & STAT_WEN))
+ ERR("SIM: PP without WEN, ignoring... \n");
+ break;
+ case CMD_SE:
+ case CMD_BE32K:
+ case CMD_BE:
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in SE/BE !\n");
+ if (!(sim_sr & STAT_WEN))
+ ERR("SIM: SE/BE without WEN, ignoring... \n");
+ sim_state = sim_state_erase_addr;
+ switch(cmd) {
+ case CMD_SE: sim_er_size = 0x1000; break;
+ case CMD_BE32K: sim_er_size = 0x8000; break;
+ case CMD_BE: sim_er_size = 0x10000; break;
+ }
+ break;
+ case CMD_CE:
+ if (!(sim_sr & STAT_WEN)) {
+ ERR("SIM: CE without WEN, ignoring... \n");
+ break;
+ }
+ memset(sim_image, 0xff, sim_image_sz);
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ break;
+ default:
+ ERR("SIM: Unsupported command %02x\n", cmd);
+ return -1;
+ }
+ return 0;
+}
+
+static void sim_end_cmd(void)
+{
+ /* For write and sector/block erase, set WIP & clear WEN here */
+ if (sim_state == sim_state_write_data) {
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ }
+ sim_state = sim_state_idle;
+}
+
+static bool sim_do_address(const uint8_t **buf, uint32_t *len)
+{
+ uint8_t asize = sim_fl_4b ? 4 : 3;
+ const uint8_t *p = *buf;
+
+ while(*len) {
+ sim_addr = (sim_addr << 8) | *(p++);
+ *buf = p;
+ *len = *len - 1;
+ sim_index++;
+ if (sim_index >= asize)
+ return true;
+ }
+ return false;
+}
+
+static int sim_wbytes(const void *buf, uint32_t len)
+{
+ const uint8_t *b = buf;
+ bool addr_complete;
+
+ again:
+ switch(sim_state) {
+ case sim_state_read_addr:
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ sim_state = sim_state_read_data;
+ sim_index = 0;
+ if (len)
+ goto again;
+ }
+ break;
+ case sim_state_write_addr:
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ sim_state = sim_state_write_data;
+ sim_index = 0;
+ if (len)
+ goto again;
+ }
+ break;
+ case sim_state_write_data:
+ if (!(sim_sr & STAT_WEN))
+ break;
+ while(len--) {
+ uint8_t c = *(b++);
+ if (sim_addr >= sim_image_sz) {
+ ERR("SIM: Write past end of flash\n");
+ return -1;
+ }
+ /* Flash write only clears bits */
+ sim_image[sim_addr] &= c;
+ sim_addr = (sim_addr & 0xffffff00) |
+ ((sim_addr + 1) & 0xff);
+ }
+ break;
+ case sim_state_erase_addr:
+ if (!(sim_sr & STAT_WEN))
+ break;
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ memset(sim_image + sim_addr, 0xff, sim_er_size);
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ sim_state = sim_state_erase_done;
+ }
+ break;
+ default:
+ ERR("SIM: Write in wrong state %d\n", sim_state);
+ return -1;
+ }
+ return 0;
+}
+
+static int sim_rbytes(void *buf, uint32_t len)
+{
+ uint8_t *b = buf;
+
+ switch(sim_state) {
+ case sim_state_rdid:
+ while(len--) {
+ switch(sim_index) {
+ case 0:
+ *(b++) = 0x55;
+ break;
+ case 1:
+ *(b++) = 0xaa;
+ break;
+ case 2:
+ *(b++) = 0x55;
+ break;
+ default:
+ ERR("SIM: RDID index %d\n", sim_index);
+ *(b++) = 0;
+ break;
+ }
+ sim_index++;
+ }
+ break;
+ case sim_state_rdsr:
+ while(len--) {
+ *(b++) = sim_sr;
+ if (sim_index > 0)
+ ERR("SIM: RDSR index %d\n", sim_index);
+ sim_index++;
+
+ /* If WIP was 1, clear it, ie, simulate write/erase
+ * completion
+ */
+ sim_sr &= ~STAT_WIP;
+ }
+ break;
+ case sim_state_read_data:
+ while(len--) {
+ if (sim_addr >= sim_image_sz) {
+ ERR("SIM: Read past end of flash\n");
+ return -1;
+ }
+ *(b++) = sim_image[sim_addr++];
+ }
+ break;
+ default:
+ ERR("SIM: Read in wrong state %d\n", sim_state);
+ return -1;
+ }
+ return 0;
+}
+
+static int sim_send_addr(uint32_t addr)
+{
+ const void *ap;
+
+ /* Layout address MSB first in memory */
+ addr = cpu_to_be32(addr);
+
+ /* Send the right amount of bytes */
+ ap = (char *)&addr;
+
+ if (sim_ct_4b)
+ return sim_wbytes(ap, 4);
+ else
+ return sim_wbytes(ap + 1, 3);
+}
+
+static int sim_cmd_rd(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd,
+ bool has_addr, uint32_t addr, void *buffer,
+ uint32_t size)
+{
+ int rc;
+
+ rc = sim_start_cmd(cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = sim_send_addr(addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = sim_rbytes(buffer, size);
+ bail:
+ sim_end_cmd();
+ return rc;
+}
+
+static int sim_cmd_wr(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd,
+ bool has_addr, uint32_t addr, const void *buffer,
+ uint32_t size)
+{
+ int rc;
+
+ rc = sim_start_cmd(cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = sim_send_addr(addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = sim_wbytes(buffer, size);
+ bail:
+ sim_end_cmd();
+ return rc;
+}
+
+static int sim_set_4b(struct spi_flash_ctrl *ctrl __unused, bool enable)
+{
+ sim_ct_4b = enable;
+
+ return 0;
+}
+
+static int sim_read(struct spi_flash_ctrl *ctrl __unused, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in autoread !\n");
+ if ((pos + len) < pos)
+ return -1;
+ if ((pos + len) > sim_image_sz)
+ return -1;
+ memcpy(buf, sim_image + pos, len);
+ return 0;
+};
+
+struct spi_flash_ctrl sim_ctrl = {
+ .cmd_wr = sim_cmd_wr,
+ .cmd_rd = sim_cmd_rd,
+ .set_4b = sim_set_4b,
+ .read = sim_read,
+};
+
+int main(void)
+{
+ struct flash_chip *fl;
+ uint32_t total_size, erase_granule;
+ const char *name;
+ uint16_t *test;
+ int i, rc;
+
+ sim_image = malloc(sim_image_sz);
+ memset(sim_image, 0xff, sim_image_sz);
+ test = malloc(0x10000 * 2);
+
+ rc = flash_init(&sim_ctrl, &fl);
+ if (rc) {
+ ERR("flash_init failed with err %d\n", rc);
+ exit(1);
+ }
+ rc = flash_get_info(fl, &name, &total_size, &erase_granule);
+ if (rc) {
+ ERR("flash_get_info failed with err %d\n", rc);
+ exit(1);
+ }
+
+ /* Make up a test pattern */
+ for (i=0; i<0x10000;i++)
+ test[i] = cpu_to_be16(i);
+
+ /* Write 64k of stuff at 0 and at 128k */
+ printf("Writing test patterns...\n");
+ flash_smart_write(fl, 0, test, 0x10000);
+ flash_smart_write(fl, 0x20000, test, 0x10000);
+
+ /* Write "Hello world" straddling the 64k boundary */
+#define HW "Hello World"
+ printf("Writing test string...\n");
+ flash_smart_write(fl, 0xfffc, HW, sizeof(HW));
+
+ /* Check result */
+ if (memcmp(sim_image + 0xfffc, HW, sizeof(HW))) {
+ ERR("Test string mismatch !\n");
+ exit(1);
+ }
+ printf("Test string pass\n");
+ if (memcmp(sim_image, test, 0xfffc)) {
+ ERR("Test pattern mismatch !\n");
+ exit(1);
+ }
+ printf("Test pattern pass\n");
+ flash_exit(fl);
+
+ return 0;
+}
+
diff --git a/op-flasher/pflash/pflash.c b/op-flasher/pflash/pflash.c
new file mode 100644
index 0000000..09359fb
--- /dev/null
+++ b/op-flasher/pflash/pflash.c
@@ -0,0 +1,793 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+#include "progress.h"
+#include "io.h"
+#include "ast.h"
+#include "sfc-ctrl.h"
+
+#define __aligned(x) __attribute__((aligned(x)))
+
+#define PFLASH_VERSION "0.8.6"
+
+static bool must_confirm = false;
+static bool dummy_run;
+static bool need_relock;
+static bool bmc_flash;
+#ifdef __powerpc__
+static bool using_sfc;
+#endif
+
+#define FILE_BUF_SIZE 0x10000
+static uint8_t file_buf[FILE_BUF_SIZE] __aligned(0x1000);
+
+static struct spi_flash_ctrl *fl_ctrl;
+static struct flash_chip *fl_chip;
+static struct ffs_handle *ffsh;
+static uint32_t fl_total_size, fl_erase_granule;
+static const char *fl_name;
+static int32_t ffs_index = -1;
+
+static void check_confirm(void)
+{
+ char yes[8], *p;
+
+ if (!must_confirm)
+ return;
+
+ printf("WARNING ! This will modify your %s flash chip content !\n",
+ bmc_flash ? "BMC" : "HOST");
+ printf("Enter \"yes\" to confirm:");
+ memset(yes, 0, sizeof(yes));
+ if (!fgets(yes, 7, stdin))
+ exit(1);
+ p = strchr(yes, 10);
+ if (p)
+ *p = 0;
+ p = strchr(yes, 13);
+ if (p)
+ *p = 0;
+ if (strcmp(yes, "yes")) {
+ printf("Operation cancelled !\n");
+ exit(1);
+ }
+ must_confirm = false;
+}
+
+static void print_flash_info(void)
+{
+ uint32_t i;
+ int rc;
+
+ printf("Flash info:\n");
+ printf("-----------\n");
+ printf("Name = %s\n", fl_name);
+ printf("Total size = %dMB \n", fl_total_size >> 20);
+ printf("Erase granule = %dKB \n", fl_erase_granule >> 10);
+
+ if (bmc_flash)
+ return;
+
+ if (!ffsh) {
+ rc = ffs_open_flash(fl_chip, 0, 0, &ffsh);
+ if (rc) {
+ fprintf(stderr, "Error %d opening ffs !\n", rc);
+ ffsh = NULL;
+ }
+ }
+ if (!ffsh)
+ return;
+
+ printf("\n");
+ printf("Partitions:\n");
+ printf("-----------\n");
+
+ for (i = 0;; i++) {
+ uint32_t start, size, act, end;
+ char *name;
+
+ rc = ffs_part_info(ffsh, i, &name, &start, &size, &act);
+ if (rc == FFS_ERR_PART_NOT_FOUND)
+ break;
+ if (rc) {
+ fprintf(stderr, "Error %d scanning partitions\n", rc);
+ break;
+ }
+ end = start + size;
+ printf("ID=%02d %15s %08x..%08x (actual=%08x)\n",
+ i, name, start, end, act);
+ free(name);
+ }
+}
+
+static void lookup_partition(const char *name)
+{
+ uint32_t index;
+ int rc;
+
+ /* Open libffs if needed */
+ if (!ffsh) {
+ rc = ffs_open_flash(fl_chip, 0, 0, &ffsh);
+ if (rc) {
+ fprintf(stderr, "Error %d opening ffs !\n", rc);
+ exit(1);
+ }
+ }
+
+ /* Find partition */
+ rc = ffs_lookup_part(ffsh, name, &index);
+ if (rc == FFS_ERR_PART_NOT_FOUND) {
+ fprintf(stderr, "Partition '%s' not found !\n", name);
+ exit(1);
+ }
+ if (rc) {
+ fprintf(stderr, "Error %d looking for partition '%s' !\n",
+ rc, name);
+ exit(1);
+ }
+ ffs_index = index;
+}
+
+static void erase_chip(void)
+{
+ int rc;
+
+ printf("About to erase chip !\n");
+ check_confirm();
+
+ printf("Erasing... (may take a while !) ");
+ fflush(stdout);
+
+ if (dummy_run) {
+ printf("skipped (dummy)\n");
+ return;
+ }
+
+ rc = flash_erase_chip(fl_chip);
+ if (rc) {
+ fprintf(stderr, "Error %d erasing chip\n", rc);
+ exit(1);
+ }
+
+ printf("done !\n");
+}
+
+static void erase_range(uint32_t start, uint32_t size, bool will_program)
+{
+ uint32_t done = 0;
+ int rc;
+
+ printf("About to erase 0x%08x..0x%08x !\n", start, start + size);
+ check_confirm();
+
+ if (dummy_run) {
+ printf("skipped (dummy)\n");
+ return;
+ }
+
+ printf("Erasing...\n");
+ progress_init(size >> 8);
+ while(size) {
+ /* If aligned to 64k and at least 64k, use 64k erase */
+ if ((start & 0xffff) == 0 && size >= 0x10000) {
+ rc = flash_erase(fl_chip, start, 0x10000);
+ if (rc) {
+ fprintf(stderr, "Error %d erasing 0x%08x\n",
+ rc, start);
+ exit(1);
+ }
+ start += 0x10000;
+ size -= 0x10000;
+ done += 0x10000;
+ } else {
+ rc = flash_erase(fl_chip, start, 0x1000);
+ if (rc) {
+ fprintf(stderr, "Error %d erasing 0x%08x\n",
+ rc, start);
+ exit(1);
+ }
+ start += 0x1000;
+ size -= 0x1000;
+ done += 0x1000;
+ }
+ progress_tick(done >> 8);
+ }
+ progress_end();
+
+ /* If this is a flash partition, mark it empty if we aren't
+ * going to program over it as well
+ */
+ if (ffsh && ffs_index >= 0 && !will_program) {
+ printf("Updating actual size in partition header...\n");
+ ffs_update_act_size(ffsh, ffs_index, 0);
+ }
+}
+
+static void program_file(const char *file, uint32_t start, uint32_t size)
+{
+ int fd, rc;
+ ssize_t len;
+ uint32_t actual_size = 0;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ perror("Failed to open file");
+ exit(1);
+ }
+ printf("About to program \"%s\" at 0x%08x..0x%08x !\n",
+ file, start, size);
+ check_confirm();
+
+ if (dummy_run) {
+ printf("skipped (dummy)\n");
+ return;
+ }
+
+ printf("Programming & Verifying...\n");
+ progress_init(size >> 8);
+ while(size) {
+ len = read(fd, file_buf, FILE_BUF_SIZE);
+ if (len < 0) {
+ perror("Error reading file");
+ exit(1);
+ }
+ if (len == 0)
+ break;
+ if (len > size)
+ len = size;
+ size -= len;
+ actual_size += len;
+ rc = flash_write(fl_chip, start, file_buf, len, true);
+ if (rc) {
+ if (rc == FLASH_ERR_VERIFY_FAILURE)
+ fprintf(stderr, "Verification failed for"
+ " chunk at 0x%08x\n", start);
+ else
+ fprintf(stderr, "Flash write error %d for"
+ " chunk at 0x%08x\n", rc, start);
+ exit(1);
+ }
+ start += len;
+ progress_tick(actual_size >> 8);
+ }
+ progress_end();
+ close(fd);
+
+ /* If this is a flash partition, adjust its size */
+ if (ffsh && ffs_index >= 0) {
+ printf("Updating actual size in partition header...\n");
+ ffs_update_act_size(ffsh, ffs_index, actual_size);
+ }
+}
+
+static void do_read_file(const char *file, uint32_t start, uint32_t size)
+{
+ int fd, rc;
+ ssize_t len;
+ uint32_t done = 0;
+
+ fd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 00666);
+ if (fd == -1) {
+ perror("Failed to open file");
+ exit(1);
+ }
+ printf("Reading to \"%s\" from 0x%08x..0x%08x !\n",
+ file, start, size);
+
+ progress_init(size >> 8);
+ while(size) {
+ len = size > FILE_BUF_SIZE ? FILE_BUF_SIZE : size;
+ rc = flash_read(fl_chip, start, file_buf, len);
+ if (rc) {
+ fprintf(stderr, "Flash read error %d for"
+ " chunk at 0x%08x\n", rc, start);
+ exit(1);
+ }
+ rc = write(fd, file_buf, len);
+ if (rc < 0) {
+ perror("Error writing file");
+ exit(1);
+ }
+ start += len;
+ size -= len;
+ done += len;
+ progress_tick(done >> 8);
+ }
+ progress_end();
+ close(fd);
+}
+
+static void enable_4B_addresses(void)
+{
+ int rc;
+
+ printf("Switching to 4-bytes address mode\n");
+
+ rc = flash_force_4b_mode(fl_chip, true);
+ if (rc) {
+ fprintf(stderr, "Error %d enabling 4b mode\n", rc);
+ exit(1);
+ }
+}
+
+static void disable_4B_addresses(void)
+{
+ int rc;
+
+ printf("Switching to 3-bytes address mode\n");
+
+ rc = flash_force_4b_mode(fl_chip, false);
+ if (rc) {
+ fprintf(stderr, "Error %d disabling 4b mode\n", rc);
+ exit(1);
+ }
+}
+
+static void flash_access_cleanup_bmc(void)
+{
+ if (ffsh)
+ ffs_close(ffsh);
+ flash_exit(fl_chip);
+ ast_sf_close(fl_ctrl);
+ close_devs();
+}
+
+static void flash_access_setup_bmc(bool use_lpc, bool need_write)
+{
+ int rc;
+
+ /* Open and map devices */
+ open_devs(use_lpc, true);
+
+ /* Create the AST flash controller */
+ rc = ast_sf_open(AST_SF_TYPE_BMC, &fl_ctrl);
+ if (rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ exit(1);
+ }
+
+ /* Open flash chip */
+ rc = flash_init(fl_ctrl, &fl_chip);
+ if (rc) {
+ fprintf(stderr, "Failed to open flash chip\n");
+ exit(1);
+ }
+
+ /* Setup cleanup function */
+ atexit(flash_access_cleanup_bmc);
+}
+
+static void flash_access_cleanup_pnor(void)
+{
+ /* Re-lock flash */
+ if (need_relock)
+ set_wrprotect(true);
+
+ if (ffsh)
+ ffs_close(ffsh);
+ flash_exit(fl_chip);
+#ifdef __powerpc__
+ if (using_sfc)
+ sfc_close(fl_ctrl);
+ else
+ ast_sf_close(fl_ctrl);
+#else
+ ast_sf_close(fl_ctrl);
+#endif
+ close_devs();
+}
+
+static void flash_access_setup_pnor(bool use_lpc, bool use_sfc, bool need_write)
+{
+ int rc;
+
+ /* Open and map devices */
+ open_devs(use_lpc, false);
+
+#ifdef __powerpc__
+ if (use_sfc) {
+ /* Create the SFC flash controller */
+ rc = sfc_open(&fl_ctrl);
+ if (rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ exit(1);
+ }
+ using_sfc = true;
+ } else {
+#endif
+ /* Create the AST flash controller */
+ rc = ast_sf_open(AST_SF_TYPE_PNOR, &fl_ctrl);
+ if (rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ exit(1);
+ }
+#ifdef __powerpc__
+ }
+#endif
+
+ /* Open flash chip */
+ rc = flash_init(fl_ctrl, &fl_chip);
+ if (rc) {
+ fprintf(stderr, "Failed to open flash chip\n");
+ exit(1);
+ }
+
+ /* Unlock flash (PNOR only) */
+ if (need_write)
+ need_relock = set_wrprotect(false);
+
+ /* Setup cleanup function */
+ atexit(flash_access_cleanup_pnor);
+}
+
+static void print_version(void)
+{
+ printf("Palmetto Flash tool " PFLASH_VERSION "\n");
+}
+
+static void print_help(const char *pname)
+{
+ printf("Usage: %s [options] commands...\n\n", pname);
+ printf(" Options:\n");
+ printf("\t-a address, --address=address\n");
+ printf("\t\tSpecify the start address for erasing, reading\n");
+ printf("\t\tor flashing\n\n");
+ printf("\t-s size, --size=size\n");
+ printf("\t\tSpecify the size in bytes for erasing, reading\n");
+ printf("\t\tor flashing\n\n");
+ printf("\t-P part_name, --partition=part_name\n");
+ printf("\t\tSpecify the partition whose content is to be erased\n");
+ printf("\t\tprogrammed or read. This is an alternative to -a and -s\n");
+ printf("\t\tif both -P and -s are specified, the smallest of the\n");
+ printf("\t\ttwo will be used\n\n");
+ printf("\t-f, --force\n");
+ printf("\t\tDon't ask for confirmation before erasing or flashing\n\n");
+ printf("\t-d, --dummy\n");
+ printf("\t\tDon't write to flash\n\n");
+#ifdef __powerpc__
+ printf("\t-l, --lpc\n");
+ printf("\t\tUse LPC accesses instead of PCI\n\n");
+#endif
+ printf("\t-b, --bmc\n");
+ printf("\t\tTarget BMC flash instead of host flash\n\n");
+ printf(" Commands:\n");
+ printf("\t-4, --enable-4B\n");
+ printf("\t\tSwitch the flash and controller to 4-bytes address\n");
+ printf("\t\tmode (no confirmation needed).\n\n");
+ printf("\t-3, --disable-4B\n");
+ printf("\t\tSwitch the flash and controller to 3-bytes address\n");
+ printf("\t\tmode (no confirmation needed).\n\n");
+ printf("\t-r file, --read=file\n");
+ printf("\t\tRead flash content from address into file, use -s\n");
+ printf("\t\tto specify the size to read (or it will use the source\n");
+ printf("\t\tfile size if used in conjunction with -p and -s is not\n");
+ printf("\t\tspecified). When using -r together with -e or -p, the\n");
+ printf("\t\tread will be peformed first\n\n");
+ printf("\t-E, --erase-all\n");
+ printf("\t\tErase entire flash chip\n");
+ printf("\t\t(Not supported on all chips/controllers)\n\n");
+ printf("\t-e, --erase\n");
+ printf("\t\tErase the specified region. If size or address are not\n");
+ printf("\t\tspecified, but \'--program\' is used, then the file\n");
+ printf("\t\tsize will be used (rounded to an erase block) and the\n");
+ printf("\t\taddress defaults to 0.\n\n");
+ printf("\t-p file, --program=file\n");
+ printf("\t\tWill program the file to flash. If the address is not\n");
+ printf("\t\tspecified, it will use 0. If the size is not specified\n");
+ printf("\t\tit will use the file size. Otherwise it will limit to\n");
+ printf("\t\tthe specified size (whatever is smaller). If used in\n");
+ printf("\t\tconjunction with any erase command, the erase will\n");
+ printf("\t\ttake place first.\n\n");
+ printf("\t-t, --tune\n");
+ printf("\t\tJust tune the flash controller & access size\n");
+ printf("\t\t(Implicit for all other operations)\n\n");
+ printf("\t-i, --info\n");
+ printf("\t\tDisplay some information about the flash.\n\n");
+ printf("\t-h, --help\n");
+ printf("\t\tThis message.\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ const char *pname = argv[0];
+ uint32_t address = 0, read_size = 0, write_size = 0;
+ uint32_t erase_start = 0, erase_size = 0;
+ bool erase = false;
+ bool program = false, erase_all = false, info = false, do_read = false;
+ bool enable_4B = false, disable_4B = false, use_lpc = true;
+ bool show_help = false, show_version = false;
+ bool has_sfc = false, has_ast = false;
+ bool no_action = false, tune = false;
+ char *write_file = NULL, *read_file = NULL, *part_name = NULL;
+ int rc;
+
+ while(1) {
+ static struct option long_opts[] = {
+ {"address", required_argument, NULL, 'a'},
+ {"size", required_argument, NULL, 's'},
+ {"partition", required_argument, NULL, 'P'},
+ {"lpc", no_argument, NULL, 'l'},
+ {"bmc", no_argument, NULL, 'b'},
+ {"enable-4B", no_argument, NULL, '4'},
+ {"disable-4B", no_argument, NULL, '3'},
+ {"read", required_argument, NULL, 'r'},
+ {"erase-all", no_argument, NULL, 'E'},
+ {"erase", no_argument, NULL, 'e'},
+ {"program", required_argument, NULL, 'p'},
+ {"force", no_argument, NULL, 'f'},
+ {"info", no_argument, NULL, 'i'},
+ {"tune", no_argument, NULL, 't'},
+ {"dummy", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"debug", no_argument, NULL, 'g'},
+ };
+ int c, oidx = 0;
+
+ c = getopt_long(argc, argv, "a:s:P:r:43Eep:fdihlvbtg",
+ long_opts, &oidx);
+ if (c == EOF)
+ break;
+ switch(c) {
+ case 'a':
+ address = strtoul(optarg, NULL, 0);
+ break;
+ case 's':
+ read_size = write_size = strtoul(optarg, NULL, 0);
+ break;
+ case 'P':
+ part_name = strdup(optarg);
+ break;
+ case '4':
+ enable_4B = true;
+ break;
+ case '3':
+ disable_4B = true;
+ break;
+ case 'r':
+ do_read = true;
+ read_file = strdup(optarg);
+ break;
+ case 'E':
+ erase_all = erase = true;
+ break;
+ case 'e':
+ erase = true;
+ break;
+ case 'p':
+ program = true;
+ write_file = strdup(optarg);
+ break;
+ case 'f':
+ must_confirm = false;
+ break;
+ case 'd':
+ must_confirm = false;
+ dummy_run = true;
+ break;
+ case 'i':
+ info = true;
+ break;
+ case 'l':
+ use_lpc = true;
+ break;
+ case 'b':
+ bmc_flash = true;
+ break;
+ case 't':
+ tune = true;
+ break;
+ case 'v':
+ show_version = true;
+ break;
+ case 'h':
+ show_help = show_version = true;
+ break;
+ case 'g':
+ libflash_debug = true;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ /* Check if we need to access the flash at all (which will
+ * also tune them as a side effect
+ */
+ no_action = !erase && !program && !info && !do_read &&
+ !enable_4B && !disable_4B && !tune;
+
+ /* Nothing to do, if we didn't already, print usage */
+ if (no_action && !show_version)
+ show_help = show_version = true;
+
+ if (show_version)
+ print_version();
+ if (show_help)
+ print_help(pname);
+
+ if (no_action)
+ return 0;
+
+ /* --enable-4B and --disable-4B are mutually exclusive */
+ if (enable_4B && disable_4B) {
+ fprintf(stderr, "--enable-4B and --disable-4B are mutually"
+ " exclusive !\n");
+ exit(1);
+ }
+
+ /* 4B not supported on BMC flash */
+ if (enable_4B && bmc_flash) {
+ fprintf(stderr, "--enable-4B not supported on BMC flash !\n");
+ exit(1);
+ }
+
+ /* partitions not supported on BMC flash */
+ if (part_name && bmc_flash) {
+ fprintf(stderr, "--partition not supported on BMC flash !\n");
+ exit(1);
+ }
+
+ /* part-name and erase-all make no sense together */
+ if (part_name && erase_all) {
+ fprintf(stderr, "--partition and --erase-all are mutually"
+ " exclusive !\n");
+ exit(1);
+ }
+
+ /* Read command should always come with a file */
+ if (do_read && !read_file) {
+ fprintf(stderr, "Read with no file specified !\n");
+ exit(1);
+ }
+
+ /* Program command should always come with a file */
+ if (program && !write_file) {
+ fprintf(stderr, "Program with no file specified !\n");
+ exit(1);
+ }
+
+ /* If both partition and address specified, error out */
+ if (address && part_name) {
+ fprintf(stderr, "Specify partition or address, not both !\n");
+ exit(1);
+ }
+
+ /* If file specified but not size, get size from file
+ */
+ if (write_file && !write_size) {
+ struct stat stbuf;
+
+ if (stat(write_file, &stbuf)) {
+ perror("Failed to get file size");
+ exit(1);
+ }
+ write_size = stbuf.st_size;
+ }
+
+ /* Check platform */
+ check_platform(&has_sfc, &has_ast);
+
+ /* Prepare for access */
+ if (bmc_flash) {
+ if (!has_ast) {
+ fprintf(stderr, "No BMC on this platform\n");
+ exit(1);
+ }
+ flash_access_setup_bmc(use_lpc, erase || program);
+ } else {
+ if (!has_ast && !has_sfc) {
+ fprintf(stderr, "No BMC nor SFC on this platform\n");
+ exit(1);
+ }
+ flash_access_setup_pnor(use_lpc, has_sfc, erase || program);
+ }
+
+ rc = flash_get_info(fl_chip, &fl_name,
+ &fl_total_size, &fl_erase_granule);
+ if (rc) {
+ fprintf(stderr, "Error %d getting flash info\n", rc);
+ exit(1);
+ }
+
+ /* If -t is passed, then print a nice message */
+ if (tune)
+ printf("Flash and controller tuned\n");
+
+ /* If read specified and no read_size, use flash size */
+ if (do_read && !read_size && !part_name)
+ read_size = fl_total_size;
+
+ /* We have a partition specified, grab the details */
+ if (part_name)
+ lookup_partition(part_name);
+
+ /* We have a partition, adjust read/write size if needed */
+ if (ffsh && ffs_index >= 0) {
+ uint32_t pstart, pmaxsz, pactsize;
+ int rc;
+
+ rc = ffs_part_info(ffsh, ffs_index, NULL,
+ &pstart, &pmaxsz, &pactsize);
+ if (rc) {
+ fprintf(stderr,"Failed to get partition info\n");
+ exit(1);
+ }
+
+ /* Read size is obtained from partition "actual" size */
+ if (!read_size)
+ read_size = pactsize;
+
+ /* Write size is max size of partition */
+ if (!write_size)
+ write_size = pmaxsz;
+
+ /* Crop write size to partition size */
+ if (write_size > pmaxsz) {
+ printf("WARNING: Size (%d bytes) larger than partition"
+ " (%d bytes), cropping to fit\n",
+ write_size, pmaxsz);
+ write_size = pmaxsz;
+ }
+
+ /* If erasing, check partition alignment */
+ if (erase && ((pstart | pmaxsz) & 0xfff)) {
+ fprintf(stderr,"Partition not aligned properly\n");
+ exit(1);
+ }
+
+ /* Set address */
+ address = pstart;
+ }
+
+ /* Align erase boundaries */
+ if (erase && !erase_all) {
+ uint32_t mask = 0xfff;
+ uint32_t erase_end;
+
+ /* Dummy size for erase, will be adjusted later */
+ if (!write_size)
+ write_size = 1;
+ erase_start = address & ~mask;
+ erase_end = ((address + write_size) + mask) & ~mask;
+ erase_size = erase_end - erase_start;
+
+ if (erase_start != address || erase_size != write_size)
+ fprintf(stderr, "WARNING: Erase region adjusted"
+ " to 0x%08x..0x%08x\n",
+ erase_start, erase_end);
+ }
+
+ /* Process commands */
+ if (enable_4B)
+ enable_4B_addresses();
+ if (disable_4B)
+ disable_4B_addresses();
+ if (info)
+ print_flash_info();
+ if (do_read)
+ do_read_file(read_file, address, read_size);
+ if (erase_all)
+ erase_chip();
+ else if (erase)
+ erase_range(erase_start, erase_size, program);
+ if (program)
+ program_file(write_file, address, write_size);
+
+ return 0;
+}
diff --git a/op-flasher/pflash/powerpc_io.c b/op-flasher/pflash/powerpc_io.c
new file mode 100644
index 0000000..1aa224b
--- /dev/null
+++ b/op-flasher/pflash/powerpc_io.c
@@ -0,0 +1,341 @@
+#define _GNU_SOURCE /* for strcasestr */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <byteswap.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include "io.h"
+
+/* Big endian warning/note:
+ *
+ * The register accessors return byteswapped data for registers
+ */
+uint32_t (*ast_ahb_readl)(uint32_t offset);
+void (*ast_ahb_writel)(uint32_t val, uint32_t offset);
+int (*ast_copy_to_ahb)(uint32_t reg, const void *src, uint32_t len);
+int (*ast_copy_from_ahb)(void *dst, uint32_t reg, uint32_t len);
+
+static enum ppc_platform {
+ plat_unknown,
+ plat_rhesus,
+ plat_ast_bmc,
+} ppc_platform;
+
+static int lpc_io_fd = -1, lpc_fw_fd = -1;
+static uint32_t lpc_old_flash_reg;
+static uint32_t ahb_flash_base, ahb_flash_size, lpc_flash_offset;
+
+static void lpc_outb(uint8_t val, uint16_t port)
+{
+ int rc;
+
+ lseek(lpc_io_fd, port, SEEK_SET);
+ rc = write(lpc_io_fd, &val, 1);
+ if (rc != 1) {
+ perror("Can't write to LPC IO");
+ exit(1);
+ }
+}
+
+static uint8_t lpc_inb(uint16_t port)
+{
+ uint8_t val;
+ int rc;
+
+ lseek(lpc_io_fd, port, SEEK_SET);
+ rc = read(lpc_io_fd, &val, 1);
+ if (rc != 1) {
+ perror("Can't read from LPC IO");
+ exit(1);
+ }
+ return val;
+}
+
+int lpc_fw_write32(uint32_t val, uint32_t addr)
+{
+ int rc;
+
+ /* The value passed in is in big endian always */
+ lseek(lpc_fw_fd, addr, SEEK_SET);
+ rc = write(lpc_fw_fd, &val, 4);
+ if (rc != 4) {
+ perror("Can't write to LPC FW");
+ exit(1);
+ }
+ return 0;
+}
+
+int lpc_fw_read32(uint32_t *val, uint32_t addr)
+{
+ int rc;
+
+ lseek(lpc_fw_fd, addr, SEEK_SET);
+ rc = read(lpc_fw_fd, val, 4);
+ if (rc != 4) {
+ perror("Can't read from LPC FW");
+ exit(1);
+ }
+ return 0;
+}
+
+static void lpc_sio_outb(uint8_t val, uint8_t reg)
+{
+ lpc_outb(reg, 0x2e);
+ lpc_outb(val, 0x2f);
+}
+
+static uint8_t lpc_sio_inb(uint8_t reg)
+{
+ lpc_outb(reg, 0x2e);
+ return lpc_inb(0x2f);
+}
+
+static void lpc_ahb_prep(uint32_t reg, uint8_t type)
+{
+ /* Address */
+ lpc_sio_outb((reg >> 24) & 0xff, 0xf0);
+ lpc_sio_outb((reg >> 16) & 0xff, 0xf1);
+ lpc_sio_outb((reg >> 8) & 0xff, 0xf2);
+ lpc_sio_outb((reg ) & 0xff, 0xf3);
+
+ /* 4 bytes cycle */
+ lpc_sio_outb(type, 0xf8);
+}
+
+static void lpc_ahb_writel(uint32_t val, uint32_t reg)
+{
+ lpc_ahb_prep(reg, 2);
+
+ /* Write data */
+ lpc_sio_outb(val >> 24, 0xf4);
+ lpc_sio_outb(val >> 16, 0xf5);
+ lpc_sio_outb(val >> 8, 0xf6);
+ lpc_sio_outb(val , 0xf7);
+
+ /* Trigger */
+ lpc_sio_outb(0xcf, 0xfe);
+}
+
+static uint32_t lpc_ahb_readl(uint32_t reg)
+{
+ uint32_t val = 0;
+
+ lpc_ahb_prep(reg, 2);
+
+ /* Trigger */
+ lpc_sio_inb(0xfe);
+
+ /* Read results */
+ val = (val << 8) | lpc_sio_inb(0xf4);
+ val = (val << 8) | lpc_sio_inb(0xf5);
+ val = (val << 8) | lpc_sio_inb(0xf6);
+ val = (val << 8) | lpc_sio_inb(0xf7);
+
+ return val;
+}
+
+static void lpc_ahb_init(bool bmc_flash)
+{
+ uint32_t b;
+
+ /* Send SuperIO password */
+ lpc_outb(0xa5, 0x2e);
+ lpc_outb(0xa5, 0x2e);
+
+ /* Select logical dev d */
+ lpc_sio_outb(0x0d, 0x07);
+
+ /* Enable iLPC->AHB */
+ lpc_sio_outb(0x01, 0x30);
+
+ /* Save flash base */
+ lpc_old_flash_reg = b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88);
+ /* Upate flash base */
+ if (bmc_flash) {
+ ahb_flash_base = BMC_FLASH_BASE;
+ ahb_flash_size = BMC_FLASH_SIZE;
+ } else {
+ ahb_flash_base = PNOR_FLASH_BASE;
+ ahb_flash_size = PNOR_FLASH_SIZE;
+ }
+ lpc_flash_offset = 0x0e000000;
+ b = (b & 0x0000ffff) | ahb_flash_base;
+ lpc_ahb_writel(b, LPC_CTRL_BASE + 0x88);
+ b = lpc_ahb_readl(LPC_CTRL_BASE + 0x88);
+}
+
+static int lpc_ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len)
+{
+ int rc;
+
+ if (reg < ahb_flash_base ||
+ (reg + len) > (ahb_flash_base + ahb_flash_size))
+ return -1;
+ reg = (reg - ahb_flash_base) + lpc_flash_offset;
+
+ lseek(lpc_fw_fd, reg, SEEK_SET);
+ rc = read(lpc_fw_fd, dst, len);
+ if (rc != len) {
+ perror("Can't read bulk from LPC FW");
+ exit(1);
+ }
+ return 0;
+}
+
+static int lpc_ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len)
+{
+ int rc;
+
+ if (reg < ahb_flash_base ||
+ (reg + len) > (ahb_flash_base + ahb_flash_size))
+ return -1;
+ reg = (reg - ahb_flash_base) + lpc_flash_offset;
+
+ lseek(lpc_fw_fd, reg, SEEK_SET);
+ rc = write(lpc_fw_fd, src, len);
+ if (rc != len) {
+ perror("Can't write bulk from LPC FW");
+ exit(1);
+ }
+ return 0;
+}
+
+/*
+ * Write protect: TODO use custom IPMI to control lock from BMC
+ */
+static uint32_t lpc_gpio_ctl_readl(uint32_t offset)
+{
+ return lpc_ahb_readl(GPIO_CTRL_BASE + offset);
+}
+
+static void lpc_gpio_ctl_writel(uint32_t val, uint32_t offset)
+{
+ lpc_ahb_writel(val, GPIO_CTRL_BASE + offset);
+}
+
+bool set_wrprotect(bool protect)
+{
+ uint32_t reg;
+ bool was_protected;
+
+ if (ppc_platform != plat_ast_bmc)
+ return false;
+
+ reg = lpc_gpio_ctl_readl(0x20);
+ was_protected = !!(reg & 0x00004000);
+ if (protect)
+ reg |= 0x00004000; /* GPIOF[6] value */
+ else
+ reg &= ~0x00004000; /* GPIOF[6] value */
+ lpc_gpio_ctl_writel(reg, 0x20);
+ reg = lpc_gpio_ctl_readl(0x24);
+ reg |= 0x00004000; /* GPIOF[6] direction */
+ lpc_gpio_ctl_writel(reg, 0x24);
+
+ return was_protected;
+}
+
+static void open_lpc(bool bmc_flash)
+{
+ lpc_fw_fd = open("/sys/kernel/debug/powerpc/lpc/fw", O_RDWR);
+ if (lpc_fw_fd < 0) {
+ perror("can't open LPC MEM");
+ exit(1);
+ }
+
+ if (ppc_platform != plat_ast_bmc)
+ return;
+
+ lpc_io_fd = open("/sys/kernel/debug/powerpc/lpc/io", O_RDWR);
+ if (lpc_io_fd < 0) {
+ perror("can't open LPC IO");
+ exit(1);
+ }
+
+ ast_ahb_readl = lpc_ahb_readl;
+ ast_ahb_writel = lpc_ahb_writel;
+ ast_copy_to_ahb = lpc_ast_copy_to_ahb;
+ ast_copy_from_ahb = lpc_ast_copy_from_ahb;
+
+ lpc_ahb_init(bmc_flash);
+}
+
+void close_devs(void)
+{
+ if (lpc_io_fd < 0 || lpc_fw_fd < 0)
+ return;
+
+ if (ppc_platform != plat_ast_bmc)
+ return;
+
+ /* Restore flash base */
+ lpc_ahb_writel(lpc_old_flash_reg, LPC_CTRL_BASE + 0x88);
+}
+
+static void open_pci(bool bmc_flash)
+{
+ /* XXX */
+ fprintf(stderr, "WARNING: PCI access method not implemented !\n");
+ fprintf(stderr, " Use -l or --lpc\n");
+ exit(1);
+}
+
+static void identify_platform(void)
+{
+ FILE *cpuinfo;
+ char *lptr = NULL;
+ size_t lsize = 0;
+ bool found = false;
+
+ ppc_platform = plat_unknown;
+
+ cpuinfo = fopen("/proc/cpuinfo", "r");
+ if (!cpuinfo) {
+ perror("Can't open /proc/cpuinfo");
+ exit(1);
+ }
+ while(!found && getline(&lptr, &lsize, cpuinfo) >= 0) {
+ if (!strncmp(lptr, "model", 5)) {
+ if (strcasestr(lptr, "rhesus"))
+ ppc_platform = plat_rhesus;
+ else if (strcasestr(lptr, "palmetto"))
+ ppc_platform = plat_ast_bmc;
+ found = true;
+ }
+ free(lptr);
+ lptr = NULL;
+ lsize = 0;
+ }
+}
+
+void open_devs(bool use_lpc, bool bmc_flash)
+{
+ if (ppc_platform == plat_unknown) {
+ fprintf(stderr, "Unsupported platform !\n");
+ exit(1);
+ }
+
+ if (use_lpc)
+ open_lpc(bmc_flash);
+ else
+ open_pci(bmc_flash);
+}
+
+void check_platform(bool *has_sfc, bool *has_ast)
+{
+ identify_platform();
+
+ *has_sfc = ppc_platform == plat_rhesus;
+ *has_ast = ppc_platform == plat_ast_bmc;
+}
diff --git a/op-flasher/pflash/progress.c b/op-flasher/pflash/progress.c
new file mode 100644
index 0000000..3ca644f
--- /dev/null
+++ b/op-flasher/pflash/progress.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+
+static unsigned long progress_max;
+static unsigned int progress_pcent;
+static unsigned long progress_n_upd;
+static unsigned int progress_prevsec;
+static struct timespec progress_start;
+
+#define PROGRESS_CHARS 50
+
+void progress_init(unsigned long count)
+{
+ unsigned int i;
+
+ progress_max = count;
+ progress_pcent = 0;
+ progress_n_upd = ULONG_MAX;
+ progress_prevsec = UINT_MAX;
+
+ printf("\r[");
+ for (i = 0; i < PROGRESS_CHARS; i++)
+ printf(" ");
+ printf("] 0%%");
+ fflush(stdout);
+ clock_gettime(CLOCK_MONOTONIC, &progress_start);}
+
+void progress_tick(unsigned long cur)
+{
+ unsigned int pcent, i, pos, sec;
+ struct timespec now;
+
+ pcent = (cur * 100) / progress_max;
+ if (progress_pcent == pcent && cur < progress_n_upd &&
+ cur < progress_max)
+ return;
+ progress_pcent = pcent;
+ pos = (pcent * PROGRESS_CHARS) / 101;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ printf("\r[");
+ for (i = 0; i <= pos; i++)
+ printf("=");
+ for (; i < PROGRESS_CHARS; i++)
+ printf(" ");
+ printf("] %d%%", pcent);
+
+ sec = now.tv_sec - progress_start.tv_sec;
+ if (sec >= 5 && pcent > 0) {
+ unsigned int persec = cur / sec;
+ unsigned int rem_sec;
+
+ if (!persec)
+ persec = 1;
+ progress_n_upd = cur + persec;
+ rem_sec = ((sec * 100) + (pcent / 2)) / pcent - sec;
+ if (rem_sec > progress_prevsec)
+ rem_sec = progress_prevsec;
+ progress_prevsec = rem_sec;
+ if (rem_sec < 60)
+ printf(" ETA:%ds ", rem_sec);
+ else {
+ printf(" ETA:%d:%02d:%02d ",
+ rem_sec / 3600,
+ (rem_sec / 60) % 60,
+ rem_sec % 60);
+ }
+ }
+
+ fflush(stdout);
+}
+
+void progress_end(void)
+{
+ printf("\n");
+}
diff --git a/op-flasher/pflash/progress.h b/op-flasher/pflash/progress.h
new file mode 100644
index 0000000..3b6137a
--- /dev/null
+++ b/op-flasher/pflash/progress.h
@@ -0,0 +1,8 @@
+#ifndef __PROGRESS_H
+#define __PROGRESS_H
+
+void progress_init(unsigned long count);
+void progress_tick(unsigned long cur);
+void progress_end(void);
+
+#endif /* __PROGRESS_H */
diff --git a/op-flasher/pflash/sfc-ctrl.c b/op-flasher/pflash/sfc-ctrl.c
new file mode 100644
index 0000000..dfcda20
--- /dev/null
+++ b/op-flasher/pflash/sfc-ctrl.c
@@ -0,0 +1,483 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "io.h"
+
+/* Offset of SFC registers in FW space */
+#define SFC_CMDREG_OFFSET 0x00000c00
+/* Offset of SFC command buffer in FW space */
+#define SFC_CMDBUF_OFFSET 0x00000d00
+/* Offset of flash MMIO mapping in FW space */
+#define SFC_MMIO_OFFSET 0x0c000000
+
+
+/*
+ * Register definitions
+ */
+#define SFC_REG_CONF 0x10 /* CONF: Direct Access Configuration */
+#define SFC_REG_CONF_FRZE (1 << 3)
+#define SFC_REG_CONF_ECCEN (1 << 2)
+#define SFC_REG_CONF_DRCD (1 << 1)
+#define SFC_REG_CONF_FLRLD (1 << 0)
+
+#define SFC_REG_STATUS 0x0C /* STATUS : Status Reg */
+#define SFC_REG_STATUS_NX_ON_SHFT 28
+#define SFC_REG_STATUS_RWP (1 << 27)
+#define SFC_REG_STATUS_FOURBYTEAD (1 << 26)
+#define SFC_REG_STATUS_ILLEGAL (1 << 4)
+#define SFC_REG_STATUS_ECCERRCNTN (1 << 3)
+#define SFC_REG_STATUS_ECCUEN (1 << 2)
+#define SFC_REG_STATUS_DONE (1 << 0)
+
+#define SFC_REG_CMD 0x40 /* CMD : Command */
+#define SFC_REG_CMD_OPCODE_SHFT 9
+#define SFC_REG_CMD_LENGTH_SHFT 0
+
+#define SFC_REG_SPICLK 0x3C /* SPICLK: SPI clock rate config */
+#define SFC_REG_SPICLK_OUTDLY_SHFT 24
+#define SFC_REG_SPICLK_INSAMPDLY_SHFT 16
+#define SFC_REG_SPICLK_CLKHI_SHFT 8
+#define SFC_REG_SPICLK_CLKLO_SHFT 0
+
+#define SFC_REG_ADR 0x44 /* ADR : Address */
+#define SFC_REG_ERASMS 0x48 /* ERASMS : Small Erase Block Size */
+#define SFC_REG_ERASLGS 0x4C /* ERALGS : Large Erase Block Size */
+#define SFC_REG_CONF4 0x54 /* CONF4 : SPI Op Code for Small Erase */
+#define SFC_REG_CONF5 0x58 /* CONF5 : Small Erase Size config reg */
+
+#define SFC_REG_CONF8 0x64 /* CONF8 : Read Command */
+#define SFC_REG_CONF8_CSINACTIVERD_SHFT 18
+#define SFC_REG_CONF8_DUMMY_SHFT 8
+#define SFC_REG_CONF8_READOP_SHFT 0
+
+#define SFC_REG_ADRCBF 0x80 /* ADRCBF : First Intf NOR Addr Offset */
+#define SFC_REG_ADRCMF 0x84 /* ADRCMF : First Intf NOR Allocation */
+#define SFC_REG_ADRCBS 0x88 /* ADRCBS : Second Intf NOR Addr Offset */
+#define SFC_REG_ADRCMS 0x8C /* ADRCMS : Second Intf NOR Allocation */
+#define SFC_REG_OADRNB 0x90 /* OADRNB : Direct Access OBP Window Base Address */
+#define SFC_REG_OADRNS 0x94 /* OADRNS : DIrect Access OPB Window Size */
+
+#define SFC_REG_CHIPIDCONF 0x9C /* CHIPIDCONF : config ChipId CMD */
+#define SFC_REG_CHIPIDCONF_OPCODE_SHFT 24
+#define SFC_REG_CHIPIDCONF_READ (1 << 23)
+#define SFC_REG_CHIPIDCONF_WRITE (1 << 22)
+#define SFC_REG_CHIPIDCONF_USE_ADDR (1 << 21)
+#define SFC_REG_CHIPIDCONF_DUMMY_SHFT 16
+#define SFC_REG_CHIPIDCONF_LEN_SHFT 0
+
+/*
+ * SFC Opcodes
+ */
+#define SFC_OP_READRAW 0x03 /* Read Raw */
+#define SFC_OP_WRITERAW 0x02 /* Write Raw */
+#define SFC_OP_ERASM 0x32 /* Erase Small */
+#define SFC_OP_ERALG 0x34 /* Erase Large */
+#define SFC_OP_ENWRITPROT 0x53 /* Enable WRite Protect */
+#define SFC_OP_CHIPID 0x1F /* Get Chip ID */
+#define SFC_OP_STATUS 0x05 /* Get Status */
+#define SFC_OP_TURNOFF 0x5E /* Turn Off */
+#define SFC_OP_TURNON 0x50 /* Turn On */
+#define SFC_OP_ABORT 0x6F /* Super-Abort */
+#define SFC_OP_START4BA 0x37 /* Start 4BA */
+#define SFC_OP_END4BA 0x69 /* End 4BA */
+
+/* Command buffer size */
+#define SFC_CMDBUF_SIZE 256
+
+struct sfc_ctrl {
+ /* Erase sizes */
+ uint32_t small_er_size;
+ uint32_t large_er_size;
+
+ /* Current 4b mode */
+ bool mode_4b;
+
+ /* Callbacks */
+ struct spi_flash_ctrl ops;
+};
+
+/* Command register support */
+static inline int sfc_reg_read(uint8_t reg, uint32_t *val)
+{
+ uint32_t tmp;
+ int rc;
+
+ *val = 0xffffffff;
+ rc = lpc_fw_read32(&tmp, SFC_CMDREG_OFFSET + reg);
+ if (rc)
+ return rc;
+ *val = be32_to_cpu(tmp);
+ return 0;
+}
+
+static inline int sfc_reg_write(uint8_t reg, uint32_t val)
+{
+ return lpc_fw_write32(cpu_to_be32(val), SFC_CMDREG_OFFSET + reg);
+}
+
+static int sfc_buf_write(uint32_t len, const void *data)
+{
+ uint32_t tmp, off = 0;
+ int rc;
+
+ if (len > SFC_CMDBUF_SIZE)
+ return FLASH_ERR_PARM_ERROR;
+
+ while (len >= 4) {
+ tmp = *(const uint32_t *)data;
+ rc = lpc_fw_write32(tmp, SFC_CMDBUF_OFFSET + off);
+ if (rc)
+ return rc;
+ off += 4;
+ len -= 4;
+ data += 4;
+ }
+ if (!len)
+ return 0;
+
+ /* lpc_fw_write operates on BE values so that's what we layout
+ * in memory with memcpy. The swap in the register on LE doesn't
+ * matter, the result in memory will be in the right order.
+ */
+ tmp = -1;
+ memcpy(&tmp, data, len);
+ return lpc_fw_write32(tmp, SFC_CMDBUF_OFFSET + off);
+}
+
+static int sfc_buf_read(uint32_t len, void *data)
+{
+ uint32_t tmp, off = 0;
+ int rc;
+
+ if (len > SFC_CMDBUF_SIZE)
+ return FLASH_ERR_PARM_ERROR;
+
+ while (len >= 4) {
+ rc = lpc_fw_read32(data, SFC_CMDBUF_OFFSET + off);
+ if (rc)
+ return rc;
+ off += 4;
+ len -= 4;
+ data += 4;
+ }
+ if (!len)
+ return 0;
+
+ rc = lpc_fw_read32(&tmp, SFC_CMDBUF_OFFSET + off);
+ if (rc)
+ return rc;
+ /* We know tmp contains a big endian value, so memcpy is
+ * our friend here
+ */
+ memcpy(data, &tmp, len);
+ return 0;
+}
+
+/* Polls until SFC indicates command is complete */
+static int sfc_poll_complete(void)
+{
+ uint32_t status;
+
+ /* XXX Add timeout */
+ do {
+ int rc;
+
+ rc = sfc_reg_read(SFC_REG_STATUS, &status);
+ if (rc)
+ return rc;
+ if (status & SFC_REG_STATUS_DONE)
+ break;
+
+ } while (true);
+
+ return 0;
+}
+
+static int sfc_exec_command(uint8_t opcode, uint32_t length)
+{
+ int rc = 0;
+ uint32_t cmd_reg = 0;
+
+ if (opcode > 0x7f || length > 0x1ff)
+ return FLASH_ERR_PARM_ERROR;
+
+ /* Write command register to start execution */
+ cmd_reg |= (opcode << SFC_REG_CMD_OPCODE_SHFT);
+ cmd_reg |= (length << SFC_REG_CMD_LENGTH_SHFT);
+ rc = sfc_reg_write(SFC_REG_CMD, cmd_reg);
+ if (rc)
+ return rc;
+
+ /* Wait for command to complete */
+ return sfc_poll_complete();
+}
+
+static int sfc_chip_id(struct spi_flash_ctrl *ctrl, uint8_t *id_buf,
+ uint32_t *id_size)
+{
+ uint32_t idconf;
+ int rc;
+
+ if ((*id_size) < 3)
+ return FLASH_ERR_PARM_ERROR;
+
+ /*
+ * XXX This will not work in locked down mode but we assume that
+ * in this case, the chip ID command is already properly programmed
+ * and the SFC will ignore this. However I haven't verified...
+ */
+ idconf = ((uint64_t)CMD_RDID) << SFC_REG_CHIPIDCONF_OPCODE_SHFT;
+ idconf |= SFC_REG_CHIPIDCONF_READ;
+ idconf |= (3ul << SFC_REG_CHIPIDCONF_LEN_SHFT);
+ (void)sfc_reg_write(SFC_REG_CHIPIDCONF, idconf);
+
+ /* Perform command */
+ rc = sfc_exec_command(SFC_OP_CHIPID, 0);
+ if (rc)
+ return rc;
+
+ /* Read chip ID */
+ rc = sfc_buf_read(3, id_buf);
+ if (rc)
+ return rc;
+ *id_size = 3;
+
+ return 0;
+}
+
+
+static int sfc_read(struct spi_flash_ctrl *ctrl, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ while(len) {
+ uint32_t chunk = len;
+ int rc;
+
+ if (chunk > SFC_CMDBUF_SIZE)
+ chunk = SFC_CMDBUF_SIZE;
+ rc = sfc_reg_write(SFC_REG_ADR, pos);
+ if (rc)
+ return rc;
+ rc = sfc_exec_command(SFC_OP_READRAW, chunk);
+ if (rc)
+ return rc;
+ rc = sfc_buf_read(chunk, buf);
+ if (rc)
+ return rc;
+ len -= chunk;
+ pos += chunk;
+ buf += chunk;
+ }
+ return 0;
+}
+
+static int sfc_write(struct spi_flash_ctrl *ctrl, uint32_t addr,
+ const void *buf, uint32_t size)
+{
+ uint32_t chunk;
+ int rc;
+
+ while(size) {
+ /* We shall not cross a page boundary */
+ chunk = 0x100 - (addr & 0xff);
+ if (chunk > size)
+ chunk = size;
+
+ /* Write to SFC write buffer */
+ rc = sfc_buf_write(chunk, buf);
+ if (rc)
+ return rc;
+
+ /* Program address */
+ rc = sfc_reg_write(SFC_REG_ADR, addr);
+ if (rc)
+ return rc;
+
+ /* Send command */
+ rc = sfc_exec_command(SFC_OP_WRITERAW, chunk);
+ if (rc)
+ return rc;
+
+ addr += chunk;
+ buf += chunk;
+ size -= chunk;
+ }
+ return 0;
+}
+
+static int sfc_erase(struct spi_flash_ctrl *ctrl, uint32_t addr,
+ uint32_t size)
+{
+ struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
+ uint32_t sm_mask = ct->small_er_size - 1;
+ uint32_t lg_mask = ct->large_er_size - 1;
+ uint32_t chunk;
+ uint8_t cmd;
+ int rc;
+
+ while(size) {
+ /* Choose erase size for this chunk */
+ if (((addr | size) & lg_mask) == 0) {
+ chunk = ct->large_er_size;
+ cmd = SFC_OP_ERALG;
+ } else if (((addr | size) & sm_mask) == 0) {
+ chunk = ct->small_er_size;
+ cmd = SFC_OP_ERASM;
+ } else
+ return FLASH_ERR_ERASE_BOUNDARY;
+
+ rc = sfc_reg_write(SFC_REG_ADR, addr);
+ if (rc)
+ return rc;
+ rc = sfc_exec_command(cmd, 0);
+ if (rc)
+ return rc;
+ addr += chunk;
+ size -= chunk;
+ }
+ return 0;
+}
+
+static int sfc_setup(struct spi_flash_ctrl *ctrl, uint32_t *tsize)
+{
+ struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
+ struct flash_info *info = ctrl->finfo;
+ uint32_t er_flags;
+
+ /* Keep non-erase related flags */
+ er_flags = ~FL_ERASE_ALL;
+
+ /* Add supported erase sizes */
+ if (ct->small_er_size == 0x1000 || ct->large_er_size == 0x1000)
+ er_flags |= FL_ERASE_4K;
+ if (ct->small_er_size == 0x8000 || ct->large_er_size == 0x8000)
+ er_flags |= FL_ERASE_32K;
+ if (ct->small_er_size == 0x10000 || ct->large_er_size == 0x10000)
+ er_flags |= FL_ERASE_64K;
+
+ /* Mask the flags out */
+ info->flags &= er_flags;
+
+ return 0;
+}
+
+static int sfc_set_4b(struct spi_flash_ctrl *ctrl, bool enable)
+{
+ struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
+ int rc;
+
+ rc = sfc_exec_command(enable ? SFC_OP_START4BA : SFC_OP_END4BA, 0);
+ if (rc)
+ return rc;
+ ct->mode_4b = enable;
+ return 0;
+}
+
+static void sfc_validate_er_size(uint32_t *size)
+{
+ if (*size == 0)
+ return;
+
+ /* We only support 4k, 32k and 64k */
+ if (*size != 0x1000 && *size != 0x8000 && *size != 0x10000) {
+ FL_ERR("SFC: Erase size %d bytes unsupported\n", *size);
+ *size = 0;
+ }
+}
+
+static int sfc_init(struct sfc_ctrl *ct)
+{
+ int rc;
+ uint32_t status;
+
+ /*
+ * Assumptions: The controller has been fully initialized
+ * by an earlier FW layer setting the chip ID command, the
+ * erase sizes, and configuring the timings for reads and
+ * writes.
+ *
+ * This driver is meant to be usable if the configuration
+ * is in lock down.
+ *
+ * If that wasn't the case, we could configure some sane
+ * defaults here and tuned values in setup() after the
+ * chip has been identified.
+ */
+
+ /* Read erase sizes from flash */
+ rc = sfc_reg_read(SFC_REG_ERASMS, &ct->small_er_size);
+ if (rc)
+ return rc;
+ sfc_validate_er_size(&ct->small_er_size);
+ rc = sfc_reg_read(SFC_REG_ERASLGS, &ct->large_er_size);
+ if (rc)
+ return rc;
+ sfc_validate_er_size(&ct->large_er_size);
+
+ /* No erase sizes we can cope with ? Ouch... */
+ if ((ct->small_er_size == 0 && ct->large_er_size == 0) ||
+ (ct->large_er_size && (ct->small_er_size > ct->large_er_size))) {
+ FL_ERR("SFC: No supported erase sizes !\n");
+ return FLASH_ERR_CTRL_CONFIG_MISMATCH;
+ }
+
+ FL_INF("SFC: Suppored erase sizes:");
+ if (ct->small_er_size)
+ FL_INF(" %dKB", ct->small_er_size >> 10);
+ if (ct->large_er_size)
+ FL_INF(" %dKB", ct->large_er_size >> 10);
+ FL_INF("\n");
+
+ /* Read current state of 4 byte addressing */
+ rc = sfc_reg_read(SFC_REG_STATUS, &status);
+ if (rc)
+ return rc;
+ ct->mode_4b = !!(status & SFC_REG_STATUS_FOURBYTEAD);
+
+ return 0;
+}
+
+int sfc_open(struct spi_flash_ctrl **ctrl)
+{
+ struct sfc_ctrl *ct;
+ int rc;
+
+ *ctrl = NULL;
+ ct = malloc(sizeof(*ct));
+ if (!ct) {
+ FL_ERR("SFC: Failed to allocate\n");
+ return FLASH_ERR_MALLOC_FAILED;
+ }
+ memset(ct, 0, sizeof(*ct));
+ ct->ops.chip_id = sfc_chip_id;
+ ct->ops.setup = sfc_setup;
+ ct->ops.set_4b = sfc_set_4b;
+ ct->ops.read = sfc_read;
+ ct->ops.write = sfc_write;
+ ct->ops.erase = sfc_erase;
+
+ rc = sfc_init(ct);
+ if (rc)
+ goto fail;
+ *ctrl = &ct->ops;
+ return 0;
+ fail:
+ free(ct);
+ return rc;
+}
+
+void sfc_close(struct spi_flash_ctrl *ctrl)
+{
+ struct sfc_ctrl *ct = container_of(ctrl, struct sfc_ctrl, ops);
+
+ /* Free the whole lot */
+ free(ct);
+}
+
diff --git a/op-flasher/pflash/sfc-ctrl.h b/op-flasher/pflash/sfc-ctrl.h
new file mode 100644
index 0000000..77ccd42
--- /dev/null
+++ b/op-flasher/pflash/sfc-ctrl.h
@@ -0,0 +1,9 @@
+#ifndef SFC_CTRL_H
+#define SFC_CTRL_H
+
+struct spi_flash_ctrl;
+
+extern int sfc_open(struct spi_flash_ctrl **ctrl);
+extern void sfc_close(struct spi_flash_ctrl *ctrl);
+
+#endif /* SFC_CTRL_H */
OpenPOWER on IntegriCloud