aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-11-14 10:59:37 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-11-14 10:59:37 +1100
commita05118a3f2d7d775cfc1f27d0cc53a4d7aba710c (patch)
tree4b2aa9579bcd2f41493d9e8914a348cb71b6b822
parent3cf5a0962e64a0f63537ddeecf04058793fed936 (diff)
downloadskiboot-a05118a3f2d7d775cfc1f27d0cc53a4d7aba710c.zip
skiboot-a05118a3f2d7d775cfc1f27d0cc53a4d7aba710c.tar.gz
skiboot-a05118a3f2d7d775cfc1f27d0cc53a4d7aba710c.tar.bz2
Import pflash 0.8.6
We share code, it's easier to maintain it this way Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--external/pflash/Makefile44
-rw-r--r--external/pflash/TODO7
-rw-r--r--external/pflash/arm_io.c139
-rw-r--r--external/pflash/ast.h66
-rw-r--r--external/pflash/config.h19
-rwxr-xr-xexternal/pflash/get_arch.sh11
-rw-r--r--external/pflash/io.h121
-rw-r--r--external/pflash/pflash.c793
-rw-r--r--external/pflash/powerpc_io.c341
-rw-r--r--external/pflash/progress.c79
-rw-r--r--external/pflash/progress.h8
-rw-r--r--external/pflash/sfc-ctrl.h9
-rw-r--r--libflash/libflash.h17
13 files changed, 1645 insertions, 9 deletions
diff --git a/external/pflash/Makefile b/external/pflash/Makefile
new file mode 100644
index 0000000..59db535
--- /dev/null
+++ b/external/pflash/Makefile
@@ -0,0 +1,44 @@
+ARCH=$(shell ./get_arch.sh $(CROSS_COMPILE))
+
+ifeq ($(ARCH),ARCH_POWERPC)
+ ARCH_OBJS = powerpc_io.o sfc-ctrl.o
+else
+ifeq ($(ARCH),ARCH_ARM)
+ ARCH_OBJS = arm_io.o
+else
+error_arch:
+ $(error Unsupported architecture $(ARCH))
+endif
+endif
+
+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 $@
+
+all: $(EXE)
+
+.PHONY: links
+links:
+ ln -sf ../../libflash .
+ ln -sf ../../ccan .
+ ln -sf ../../hw/sfc-ctrl.c .
+ ln -sf ../../hw/ast-bmc/ast-sf-ctrl.c
+
+$(OBJS) : links
+
+$(EXE): $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
+
+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~
+ rm -f libflash ccan sfc-ctrl.c ast-sf-ctrl.c
diff --git a/external/pflash/TODO b/external/pflash/TODO
new file mode 100644
index 0000000..b30f36d
--- /dev/null
+++ b/external/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/external/pflash/arm_io.c b/external/pflash/arm_io.c
new file mode 100644
index 0000000..ad040c3
--- /dev/null
+++ b/external/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/external/pflash/ast.h b/external/pflash/ast.h
new file mode 100644
index 0000000..92cafa4
--- /dev/null
+++ b/external/pflash/ast.h
@@ -0,0 +1,66 @@
+#ifndef __AST_H
+#define __AST_H
+
+/*
+ * AHB bus registers
+ */
+
+/* SPI Flash controller #1 (BMC) */
+#define BMC_SPI_FCTL_BASE 0x1E620000
+#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)
+
+/* 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);
+
+#endif /* __SKIBOOT__ */
+
+/*
+ * SPI Flash controllers
+ */
+#define AST_SF_TYPE_PNOR 0
+#define AST_SF_TYPE_BMC 1
+
+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/external/pflash/config.h b/external/pflash/config.h
new file mode 100644
index 0000000..a132a01
--- /dev/null
+++ b/external/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/external/pflash/get_arch.sh b/external/pflash/get_arch.sh
new file mode 100755
index 0000000..18a5cef
--- /dev/null
+++ b/external/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/external/pflash/io.h b/external/pflash/io.h
new file mode 100644
index 0000000..257cfd2
--- /dev/null
+++ b/external/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/external/pflash/pflash.c b/external/pflash/pflash.c
new file mode 100644
index 0000000..908a41d
--- /dev/null
+++ b/external/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 = true;
+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/external/pflash/powerpc_io.c b/external/pflash/powerpc_io.c
new file mode 100644
index 0000000..7678161
--- /dev/null
+++ b/external/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/external/pflash/progress.c b/external/pflash/progress.c
new file mode 100644
index 0000000..1f98502
--- /dev/null
+++ b/external/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/external/pflash/progress.h b/external/pflash/progress.h
new file mode 100644
index 0000000..673b495
--- /dev/null
+++ b/external/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/external/pflash/sfc-ctrl.h b/external/pflash/sfc-ctrl.h
new file mode 100644
index 0000000..6c30759
--- /dev/null
+++ b/external/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 */
diff --git a/libflash/libflash.h b/libflash/libflash.h
index e8d357b..31d3562 100644
--- a/libflash/libflash.h
+++ b/libflash/libflash.h
@@ -19,16 +19,15 @@
#include <stdint.h>
#include <stdbool.h>
-#ifndef FL_INF
+#ifdef __SKIBOOT__
+#include <skiboot.h>
+#define FL_INF(fmt...) do { prlog(PR_INFO, fmt); } while(0)
+#define FL_DBG(fmt...) do { prlog(PR_DEBUG, fmt); } while(0)
+#define FL_ERR(fmt...) do { prlog(PR_ERR, fmt); } while(0)
+#else
+extern bool libflash_debug;
+#define FL_DBG(fmt...) do { if (libflash_debug) printf(fmt); } while(0)
#define FL_INF(fmt...) do { printf(fmt); } while(0)
-#endif
-
-#ifndef FL_DBG
-//#define FL_DBG(fmt...) do { printf(fmt); } while(0)
-#define FL_DBG(fmt...) do { } while(0)
-#endif
-
-#ifndef FL_ERR
#define FL_ERR(fmt...) do { printf(fmt); } while(0)
#endif