diff mbox

[RFC,1/3] core/flash: Add flash API

Message ID 1423220959.396253.252628446422.1.gpush@pablo
State Changes Requested
Headers show

Commit Message

Jeremy Kerr Feb. 6, 2015, 11:09 a.m. UTC
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>

---
 core/Makefile.inc                  |    2 
 core/flash.c                       |  220 +++++++++++++++++++++++++++++
 doc/device-tree/ibm,opal/flash.txt |   35 ++++
 include/opal.h                     |    5 
 include/skiboot.h                  |    6 
 5 files changed, 265 insertions(+), 3 deletions(-)

Comments

Joel Stanley Feb. 9, 2015, 1:01 a.m. UTC | #1
On Fri, Feb 6, 2015 at 9:39 PM, Jeremy Kerr <jk@ozlabs.org> wrote:
> 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

Looks good.

>
> - 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>

>
> ---
>  core/Makefile.inc                  |    2
>  core/flash.c                       |  220 +++++++++++++++++++++++++++++
>  doc/device-tree/ibm,opal/flash.txt |   35 ++++
>  include/opal.h                     |    5
>  include/skiboot.h                  |    6
>  5 files changed, 265 insertions(+), 3 deletions(-)
Joel Stanley Feb. 9, 2015, 1:07 a.m. UTC | #2
On Fri, Feb 6, 2015 at 9:39 PM, Jeremy Kerr <jk@ozlabs.org> wrote:
> diff --git a/core/flash.c b/core/flash.c
> new file mode 100644
> index 0000000..4a4198d
> --- /dev/null
> +++ b/core/flash.c

> +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) {

Do we want to check for overflow here?

> +               rc = OPAL_PARAMETER;
> +               goto err;
> +       }
> +
> +       mask = flash->block_size - 1;
> +       if (size & mask || offset & mask) {
> +               rc = OPAL_PARAMETER;
> +               goto err;
> +       }
> +
Jeremy Kerr Feb. 9, 2015, 1:57 a.m. UTC | #3
Hi Joel,

> On Fri, Feb 6, 2015 at 9:39 PM, Jeremy Kerr <jk@ozlabs.org> wrote:
>> diff --git a/core/flash.c b/core/flash.c
>> new file mode 100644
>> index 0000000..4a4198d
>> --- /dev/null
>> +++ b/core/flash.c
> 
>> +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)
>> +{

...

>> +
>> +       if (size >= flash->size || offset >= flash->size
>> +                       || offset + size >= flash->size) {
> 
> Do we want to check for overflow here?

For this to overflow, we'd need:

  offset + size > 0xffffffffffffffff

but we've already tested that both size and offset are less than
flash->size, which is a u32, so has a maximum of 0xffffffff. The largest
that offset + size can be is 0x1fffffffe, which won't overflow the u64.

Cheers,


Jeremy
Stewart Smith Feb. 9, 2015, 3:41 a.m. UTC | #4
Jeremy Kerr <jk@ozlabs.org> writes:
> +opal_call(OPAL_FLASH_READ, opal_flash_read, 5);
> +opal_call(OPAL_FLASH_WRITE, opal_flash_write, 5);
> +opal_call(OPAL_FLASH_WRITE, opal_flash_erase, 4);

Last one should be OPAL_FLASH_ERASE?
Jeremy Kerr Feb. 9, 2015, 3:58 a.m. UTC | #5
Hi Stewart,

> Jeremy Kerr <jk@ozlabs.org> writes:
>> +opal_call(OPAL_FLASH_READ, opal_flash_read, 5);
>> +opal_call(OPAL_FLASH_WRITE, opal_flash_write, 5);
>> +opal_call(OPAL_FLASH_WRITE, opal_flash_erase, 4);
> 
> Last one should be OPAL_FLASH_ERASE?

Yes. Yes it should.

Will update this in a v2.

Thanks!


Jeremy
diff mbox

Patch

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..4a4198d
--- /dev/null
+++ b/core/flash.c
@@ -0,0 +1,220 @@ 
+/* 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);
+	}
+
+	unlock(&flash_lock);
+
+	opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc);
+	return OPAL_ASYNC_COMPLETION;
+
+err:
+	unlock(&flash_lock);
+	return rc;
+}
+
+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_WRITE, 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/include/opal.h b/include/opal.h
index 2da0929..8c7a11f 100644
--- a/include/opal.h
+++ b/include/opal.h
@@ -158,7 +158,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
 
 #ifndef __ASSEMBLY__
 
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 */