add pflash
diff --git a/objects/pflash/libflash/ffs.h b/objects/pflash/libflash/ffs.h
new file mode 100644
index 0000000..36b0b00
--- /dev/null
+++ b/objects/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/objects/pflash/libflash/libffs.c b/objects/pflash/libflash/libffs.c
new file mode 100644
index 0000000..db7104d
--- /dev/null
+++ b/objects/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/objects/pflash/libflash/libffs.h b/objects/pflash/libflash/libffs.h
new file mode 100644
index 0000000..9853987
--- /dev/null
+++ b/objects/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/objects/pflash/libflash/libffs.o b/objects/pflash/libflash/libffs.o
new file mode 100644
index 0000000..4ecdc98
--- /dev/null
+++ b/objects/pflash/libflash/libffs.o
Binary files differ
diff --git a/objects/pflash/libflash/libflash-priv.h b/objects/pflash/libflash/libflash-priv.h
new file mode 100644
index 0000000..cf40e2e
--- /dev/null
+++ b/objects/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/objects/pflash/libflash/libflash.c b/objects/pflash/libflash/libflash.c
new file mode 100644
index 0000000..63cf692
--- /dev/null
+++ b/objects/pflash/libflash/libflash.c
@@ -0,0 +1,683 @@
+#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"   },

+	{ 0x20ba20, 0x04000000, FL_ERASE_4K  | FL_ERASE_64K | FL_CAN_4B |

+                                FL_ERASE_BULK | FL_MICRON_BUGS,

+                                                          "Micron N25Qx512Ax"   },

+	{ 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/objects/pflash/libflash/libflash.h b/objects/pflash/libflash/libflash.h
new file mode 100644
index 0000000..a341c54
--- /dev/null
+++ b/objects/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/objects/pflash/libflash/libflash.o b/objects/pflash/libflash/libflash.o
new file mode 100644
index 0000000..4da0d03
--- /dev/null
+++ b/objects/pflash/libflash/libflash.o
Binary files differ
diff --git a/objects/pflash/libflash/test/Makefile b/objects/pflash/libflash/test/Makefile
new file mode 100644
index 0000000..e357e20
--- /dev/null
+++ b/objects/pflash/libflash/test/Makefile
@@ -0,0 +1,2 @@
+test_flash: test-flash.c 
+	$(CC) -o $@ $^ -Wall -I../../  -include ../../config.h
diff --git a/objects/pflash/libflash/test/test-flash.c b/objects/pflash/libflash/test/test-flash.c
new file mode 100644
index 0000000..0e92d8c
--- /dev/null
+++ b/objects/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;

+}

+