[Skiboot] [PATCH 1/3 v2] core/flash: Add flash API

Jeremy Kerr jk at ozlabs.org
Tue Feb 17 16:28:12 AEDT 2015


We'd like to enable access to the system PNOR, on platforms where its
present. This change introduces an API for global flash operations:

 opal_flash_read
 opal_flash_erase
 opal_flash_write

- plus device-tree bindings to expose the flash details.

Since there are other components of the system that use the PNOR (NVRAM
and pnor_load_resource), upcoming changes will port this these over to
use the new interface.

Signed-off-by: Jeremy Kerr <jk at ozlabs.org>
Reviewed-by: Joel Stanley <joel at jms.id.au>

---
 core/Makefile.inc                       |    2 
 core/flash.c                            |  225 ++++++++++++++++++++++++
 doc/device-tree/ibm,opal/flash.txt      |   35 +++
 doc/opal-api/opal-flash-110-111-112.txt |   72 +++++++
 include/opal.h                          |    5 
 include/skiboot.h                       |    6 
 6 files changed, 342 insertions(+), 3 deletions(-)

diff --git a/core/Makefile.inc b/core/Makefile.inc
index 8540695..1a4e466 100644
--- a/core/Makefile.inc
+++ b/core/Makefile.inc
@@ -7,7 +7,7 @@ CORE_OBJS += timebase.o opal-msg.o pci.o pci-opal.o fast-reboot.o
 CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o
 CORE_OBJS += hostservices.o platform.o nvram.o flash-nvram.o hmi.o
 CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
-CORE_OBJS += timer.o i2c.o rtc.o
+CORE_OBJS += timer.o i2c.o rtc.o flash.o
 CORE=core/built-in.o
 
 CFLAGS_SKIP_core/relocate.o = -pg -fstack-protector-all
diff --git a/core/flash.c b/core/flash.c
new file mode 100644
index 0000000..a38324f
--- /dev/null
+++ b/core/flash.c
@@ -0,0 +1,225 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <skiboot.h>
+#include <lock.h>
+#include <opal.h>
+#include <opal-msg.h>
+#include <opal-api.h>
+#include <device.h>
+#include <libflash/libflash.h>
+#include <libflash/libffs.h>
+
+struct flash {
+	bool			registered;
+	struct flash_chip	*chip;
+	uint32_t		size;
+	uint32_t		block_size;
+};
+
+#define MAX_FLASH 1
+static struct flash flashes[MAX_FLASH];
+static struct flash *system_flash;
+
+/* Using a single lock as we only have one flash at present. */
+static struct lock flash_lock;
+
+static void flash_add_dt_partition_node(struct dt_node *flash_node, char *name,
+		uint32_t start, uint32_t size)
+{
+	struct dt_node *part_node;
+
+	part_node = dt_new_addr(flash_node, "partition", start);
+	dt_add_property_cells(part_node, "reg", start, size);
+	if (name && strlen(name))
+		dt_add_property_strings(part_node, "label", name);
+}
+
+static void flash_add_dt_node(struct flash *flash, int id,
+		struct ffs_handle *ffs)
+{
+	struct dt_node *flash_node;
+	int i;
+
+	flash_node = dt_new_addr(opal_node, "flash", id);
+	dt_add_property_strings(flash_node, "compatible", "ibm,opal-flash");
+	dt_add_property_cells(flash_node, "ibm,opal-id", id);
+	dt_add_property_cells(flash_node, "reg", 0, flash->size);
+	dt_add_property_cells(flash_node, "ibm,flash-block-size",
+			flash->block_size);
+
+	/* we fix to 32-bits */
+	dt_add_property_cells(flash_node, "#address-cells", 1);
+	dt_add_property_cells(flash_node, "#size-cells", 1);
+
+	if (!ffs)
+		return;
+
+	for (i = 0; ; i++) {
+		uint32_t start, size;
+		char *name;
+		int rc;
+
+		rc = ffs_part_info(ffs, i, &name, &start, NULL, &size);
+		if (rc)
+			break;
+
+		flash_add_dt_partition_node(flash_node, name, start, size);
+	}
+}
+
+int flash_register(struct flash_chip *chip, bool is_system_flash)
+{
+	uint32_t size, block_size;
+	struct ffs_handle *ffs;
+	struct flash *flash;
+	const char *name;
+	unsigned int i;
+	int rc;
+
+	rc = flash_get_info(chip, &name, &size, &block_size);
+	if (rc)
+		return rc;
+
+	prlog(PR_INFO, "FLASH: registering flash device %s "
+			"(size 0x%x, blocksize 0x%x)\n",
+			name ?: "(unnamed)", size, block_size);
+
+	lock(&flash_lock);
+	for (i = 0; i < ARRAY_SIZE(flashes); i++) {
+		if (flashes[i].registered)
+			continue;
+
+		flash = &flashes[i];
+		flash->registered = true;
+		flash->chip = chip;
+		flash->size = size;
+		flash->block_size = block_size;
+		break;
+	}
+
+	if (!flash) {
+		unlock(&flash_lock);
+		prlog(PR_ERR, "FLASH: No flash slots available\n");
+		return OPAL_RESOURCE;
+	}
+
+	rc = ffs_open_flash(chip, 0, flash->size, &ffs);
+	if (rc) {
+		prlog(PR_WARNING, "FLASH: No ffs info; "
+				"using raw device only\n");
+		ffs = NULL;
+	}
+
+	if (is_system_flash && !system_flash)
+		system_flash = flash;
+
+	flash_add_dt_node(flash, i, ffs);
+
+	ffs_close(ffs);
+
+	unlock(&flash_lock);
+
+	return OPAL_SUCCESS;
+}
+
+enum flash_op {
+	FLASH_OP_READ,
+	FLASH_OP_WRITE,
+	FLASH_OP_ERASE,
+};
+
+static int64_t opal_flash_op(uint64_t id, uint64_t offset, uint64_t buf,
+		uint64_t size, uint64_t token, enum flash_op op)
+{
+	struct flash *flash;
+	uint32_t mask;
+	int rc;
+
+	if (id >= ARRAY_SIZE(flashes))
+		return OPAL_PARAMETER;
+
+	if (!try_lock(&flash_lock))
+		return OPAL_BUSY;
+
+	flash = &flashes[id];
+	if (!flash->registered) {
+		rc = OPAL_PARAMETER;
+		goto err;
+	}
+
+	if (size >= flash->size || offset >= flash->size
+			|| offset + size >= flash->size) {
+		rc = OPAL_PARAMETER;
+		goto err;
+	}
+
+	mask = flash->block_size - 1;
+	if (size & mask || offset & mask) {
+		rc = OPAL_PARAMETER;
+		goto err;
+	}
+
+	switch (op) {
+	case FLASH_OP_READ:
+		rc = flash_read(flash->chip, offset, (void *)buf, size);
+		break;
+	case FLASH_OP_WRITE:
+		rc = flash_write(flash->chip, offset, (void *)buf, size, false);
+		break;
+	case FLASH_OP_ERASE:
+		rc = flash_erase(flash->chip, offset, size);
+		break;
+	default:
+		assert(0);
+	}
+
+	if (rc) {
+		rc = OPAL_HARDWARE;
+		goto err;
+	}
+
+	unlock(&flash_lock);
+
+	opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc);
+	return OPAL_ASYNC_COMPLETION;
+
+err:
+	unlock(&flash_lock);
+	return OPAL_HARDWARE;
+}
+
+static int64_t opal_flash_read(uint64_t id, uint64_t offset, uint64_t buf,
+		uint64_t size, uint64_t token)
+{
+	return opal_flash_op(FLASH_OP_READ, id, offset, buf, size, token);
+}
+
+static int64_t opal_flash_write(uint64_t id, uint64_t offset, uint64_t buf,
+		uint64_t size, uint64_t token)
+{
+	return opal_flash_op(FLASH_OP_WRITE, id, offset, buf, size, token);
+}
+
+static int64_t opal_flash_erase(uint64_t id, uint64_t offset, uint64_t size,
+		uint64_t token)
+{
+	return opal_flash_op(FLASH_OP_ERASE, id, offset, 0L, size, token);
+}
+
+opal_call(OPAL_FLASH_READ, opal_flash_read, 5);
+opal_call(OPAL_FLASH_WRITE, opal_flash_write, 5);
+opal_call(OPAL_FLASH_ERASE, opal_flash_erase, 4);
diff --git a/doc/device-tree/ibm,opal/flash.txt b/doc/device-tree/ibm,opal/flash.txt
new file mode 100644
index 0000000..872d623
--- /dev/null
+++ b/doc/device-tree/ibm,opal/flash.txt
@@ -0,0 +1,35 @@
+ibm,opal/flash device tree entries
+----------------------------------
+
+The flash@<n> nodes under ibm,opal describe flash devices that can be
+accessed through the OPAL_FLASH_{READ,ERASE,WRITE} interface.
+
+These interfaces take an 'id' parameter, which corresponds to the ibm,opal-id
+property of the node.
+
+The properties under a flash node are:
+
+ compatible = "ibm,opal-flash"
+ 
+ ibm,opal-id = <id>
+   - provides the index used for the OPAL_FLASH_ calls to reference this
+     flash device
+
+ reg = <0 size>
+   - the offset and size of the flash device
+
+ ibm,flash-block-size
+   - the read/write/erase block size for the flash interface. Calls
+     to read/write/erase must be aligned to the block size.
+
+ #address-cells = <1>
+ #size-cells = <1>
+   - flash devices are currently 32-bit addressable
+
+
+If valid partitions are found on the flash device, then partition@<offset>
+sub-nodes are added to the flash node. These match the Linux binding for
+flash partitions; the reg parameter contains the offset and size of the
+partition.
+
+
diff --git a/doc/opal-api/opal-flash-110-111-112.txt b/doc/opal-api/opal-flash-110-111-112.txt
new file mode 100644
index 0000000..860172b
--- /dev/null
+++ b/doc/opal-api/opal-flash-110-111-112.txt
@@ -0,0 +1,72 @@
+
+OPAL Flash calls
+----------------
+
+There are three OPAL calls for interacting with flash devices:
+
+ #define OPAL_FLASH_READ	110
+ #define OPAL_FLASH_WRITE	111
+ #define OPAL_FLASH_ERASE	112
+
+Multiple flash devices are supported by OPAL - each of these calls takes an id
+parameter, which much match an ID found in the corresponding ibm,opal/flash at n
+device tree node. See doc/device-tree/ibm,opal/flash.txt for details of
+the device tree bindings.
+
+All operations on the flash device must be aligned to the block size of the
+flash. This applies to both offset and size arguments.
+
+This interface is asynchronous; all calls require a 'token' argument. On
+success, the calls will return OPAL_ASYNC_COMPLETION, and an
+opal_async_completion message will be sent (with the appropriate token
+argument) when the operation completes.
+
+All calls share the same return values:
+
+	OPAL_ASYNC_COMPLETION - operation started, an async completion will
+		be triggered with the @token argument
+	OPAL_PARAMETER - invalid flash id
+	OPAL_PARAMETER - invalid size or offset (alignment, or access beyond end
+		of device)
+	OPAL_BUSY - flash in use
+	OPAL_HARDWARE - error accessing flash device
+
+OPAL_FLASH_READ
+---------------
+
+Parameters:
+	uint64_t id
+	uint64_t offset
+	uint64_t buffer
+	uint64_t size
+	uint64_t token
+
+Reads from the specified flash id, at the specified offset, into the buffer.
+Will trigger an async completion with token when completed.
+
+OPAL_FLASH_ERASE
+---------------
+
+Parameters:
+	uint64_t id
+	uint64_t offset
+	uint64_t size
+	uint64_t token
+
+Erases the specified flash id, at the specified offset and size.  Will trigger
+an async completion with token when completed.
+
+OPAL_FLASH_WRITE
+---------------
+
+Parameters:
+	uint64_t id
+	uint64_t offset
+	uint64_t buffer
+	uint64_t size
+	uint64_t token
+
+Writes buffer to the specified flash id, at the specified offset and size. The
+flash must be erased before being written. Will trigger an async completion with
+token when completed.
+
diff --git a/include/opal.h b/include/opal.h
index bf0365b..0add399 100644
--- a/include/opal.h
+++ b/include/opal.h
@@ -161,7 +161,10 @@
 #define OPAL_IPMI_SEND				107
 #define OPAL_IPMI_RECV				108
 #define OPAL_I2C_REQUEST			109
-#define OPAL_LAST				109
+#define OPAL_FLASH_READ				110
+#define OPAL_FLASH_WRITE			111
+#define OPAL_FLASH_ERASE			112
+#define OPAL_LAST				112
 
 /* Device tree flags */
 
diff --git a/include/skiboot.h b/include/skiboot.h
index 1b55638..dbc2057 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -196,12 +196,16 @@ extern void occ_pstates_init(void);
 extern void slw_init(void);
 extern void occ_fsp_init(void);
 
+/* flash support */
+struct flash_chip;
+extern int flash_register(struct flash_chip *chip, bool is_system_flash);
+extern bool flash_load_resource(enum resource_id id, void *buf, size_t *len);
+
 /* NVRAM support */
 extern void nvram_init(void);
 extern void nvram_read_complete(bool success);
 
 /* NVRAM on flash helper */
-struct flash_chip;
 extern int flash_nvram_init(struct flash_chip *chip, uint32_t start,
 			    uint32_t size);
 /* UART stuff */


More information about the Skiboot mailing list