diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2015-02-19 06:34:53 +0800 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2015-02-19 10:40:50 +1100 |
commit | e7d1f60eb49bfd81e1fe153cece899de2542f83e (patch) | |
tree | c530c6442149dafcbc0d1e4205eeb0a1d5e8886e | |
parent | 10a7817fce7416a44d3683d381af1066fad315cd (diff) | |
download | skiboot-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.inc | 2 | ||||
-rw-r--r-- | core/flash.c | 225 | ||||
-rw-r--r-- | doc/device-tree/ibm,opal/flash.txt | 35 | ||||
-rw-r--r-- | doc/opal-api/opal-flash-110-111-112.txt | 72 | ||||
-rw-r--r-- | include/opal.h | 5 | ||||
-rw-r--r-- | 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..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 */ |