aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2015-02-19 06:34:53 +0800
committerStewart Smith <stewart@linux.vnet.ibm.com>2015-02-19 10:40:50 +1100
commite7d1f60eb49bfd81e1fe153cece899de2542f83e (patch)
treec530c6442149dafcbc0d1e4205eeb0a1d5e8886e
parent10a7817fce7416a44d3683d381af1066fad315cd (diff)
downloadskiboot-e7d1f60eb49bfd81e1fe153cece899de2542f83e.zip
skiboot-e7d1f60eb49bfd81e1fe153cece899de2542f83e.tar.gz
skiboot-e7d1f60eb49bfd81e1fe153cece899de2542f83e.tar.bz2
core/flash: Add flash API
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@ozlabs.org> Reviewed-by: Joel Stanley <joel@jms.id.au> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r--core/Makefile.inc2
-rw-r--r--core/flash.c225
-rw-r--r--doc/device-tree/ibm,opal/flash.txt35
-rw-r--r--doc/opal-api/opal-flash-110-111-112.txt72
-rw-r--r--include/opal.h5
-rw-r--r--include/skiboot.h6
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..33e9242
--- /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(enum flash_op op, uint64_t id, uint64_t offset,
+ uint64_t buf, uint64_t size, uint64_t token)
+{
+ 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@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 0a57bd0..bdb0cc5 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 */