aboutsummaryrefslogtreecommitdiff
path: root/core/flash.c
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 /core/flash.c
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>
Diffstat (limited to 'core/flash.c')
-rw-r--r--core/flash.c225
1 files changed, 225 insertions, 0 deletions
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);