summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/arm/ccn/ccn.c13
-rw-r--r--drivers/arm/ccn/ccn_private.h1
-rw-r--r--drivers/auth/auth_mod.c16
-rw-r--r--drivers/delay_timer/delay_timer.c23
-rw-r--r--drivers/delay_timer/generic_delay_timer.c82
-rw-r--r--drivers/emmc/emmc.c347
-rw-r--r--drivers/gpio/gpio.c20
-rw-r--r--drivers/io/io_block.c412
8 files changed, 899 insertions, 15 deletions
diff --git a/drivers/arm/ccn/ccn.c b/drivers/arm/ccn/ccn.c
index 28d27098..060acdd2 100644
--- a/drivers/arm/ccn/ccn.c
+++ b/drivers/arm/ccn/ccn.c
@@ -254,6 +254,7 @@ static unsigned long long ccn_master_to_rn_id_map(unsigned long long master_map)
assert(ccn_plat_desc);
FOR_EACH_PRESENT_MASTER_INTERFACE(iface_id, master_map) {
+ assert(iface_id < ccn_plat_desc->num_masters);
/* Convert the master ID into the node ID */
node_id = ccn_plat_desc->master_to_rn_id_map[iface_id];
@@ -501,3 +502,15 @@ void ccn_program_sys_addrmap(unsigned int sn0_id,
}
}
+
+/*******************************************************************************
+ * This function returns the part0 id from the peripheralID 0 register
+ * in CCN. This id can be used to distinguish the CCN variant present in the
+ * system.
+ ******************************************************************************/
+int ccn_get_part0_id(uintptr_t periphbase)
+{
+ assert(periphbase);
+ return (int)(mmio_read_64(periphbase
+ + MN_PERIPH_ID_0_1_OFFSET) & 0xFF);
+}
diff --git a/drivers/arm/ccn/ccn_private.h b/drivers/arm/ccn/ccn_private.h
index 8b154725..fffa2ca9 100644
--- a/drivers/arm/ccn/ccn_private.h
+++ b/drivers/arm/ccn/ccn_private.h
@@ -149,6 +149,7 @@ typedef enum rn_types {
#define MN_DDC_STAT_OFFSET DOMAIN_CTRL_STAT_OFFSET
#define MN_DDC_SET_OFFSET DOMAIN_CTRL_SET_OFFSET
#define MN_DDC_CLR_OFFSET DOMAIN_CTRL_CLR_OFFSET
+#define MN_PERIPH_ID_0_1_OFFSET 0xFE0
#define MN_ID_OFFSET REGION_ID_OFFSET
/* HNF System Address Map register bit masks and shifts */
diff --git a/drivers/auth/auth_mod.c b/drivers/auth/auth_mod.c
index 41845561..88ef0b02 100644
--- a/drivers/auth/auth_mod.c
+++ b/drivers/auth/auth_mod.c
@@ -199,8 +199,9 @@ static int auth_signature(const auth_method_param_sig_t *param,
}
return_if_error(rc);
- /* If the PK is a hash of the key, retrieve the key from the image */
- if (flags & ROTPK_IS_HASH) {
+ if (flags & (ROTPK_IS_HASH | ROTPK_NOT_DEPLOYED)) {
+ /* If the PK is a hash of the key or if the ROTPK is not
+ deployed on the platform, retrieve the key from the image */
pk_hash_ptr = pk_ptr;
pk_hash_len = pk_len;
rc = img_parser_get_auth_param(img_desc->img_type,
@@ -215,9 +216,14 @@ static int auth_signature(const auth_method_param_sig_t *param,
pk_ptr, pk_len);
return_if_error(rc);
- /* Ask the crypto-module to verify the key hash */
- rc = crypto_mod_verify_hash(pk_ptr, pk_len,
- pk_hash_ptr, pk_hash_len);
+ if (flags & ROTPK_NOT_DEPLOYED) {
+ NOTICE("ROTPK is not deployed on platform. "
+ "Skipping ROTPK verification.\n");
+ } else {
+ /* Ask the crypto-module to verify the key hash */
+ rc = crypto_mod_verify_hash(pk_ptr, pk_len,
+ pk_hash_ptr, pk_hash_len);
+ }
} else {
/* Ask the crypto module to verify the signature */
rc = crypto_mod_verify_signature(data_ptr, data_len,
diff --git a/drivers/delay_timer/delay_timer.c b/drivers/delay_timer/delay_timer.c
index 0bee876f..ed7ed52e 100644
--- a/drivers/delay_timer/delay_timer.c
+++ b/drivers/delay_timer/delay_timer.c
@@ -48,19 +48,22 @@ void udelay(uint32_t usec)
(ops->clk_div != 0) &&
(ops->get_timer_value != 0));
- uint32_t start, cnt, delta, delta_us;
+ uint32_t start, delta, total_delta;
+
+ assert(usec < UINT32_MAX / ops->clk_div);
- /* counter is decreasing */
start = ops->get_timer_value();
+
+ total_delta = (usec * ops->clk_div) / ops->clk_mult;
+
do {
- cnt = ops->get_timer_value();
- if (cnt > start) {
- delta = UINT32_MAX - cnt;
- delta += start;
- } else
- delta = start - cnt;
- delta_us = (delta * ops->clk_mult) / ops->clk_div;
- } while (delta_us < usec);
+ /*
+ * If the timer value wraps around, the subtraction will
+ * overflow and it will still give the correct result.
+ */
+ delta = start - ops->get_timer_value(); /* Decreasing counter */
+
+ } while (delta < total_delta);
}
/***********************************************************
diff --git a/drivers/delay_timer/generic_delay_timer.c b/drivers/delay_timer/generic_delay_timer.c
new file mode 100644
index 00000000..4c364a30
--- /dev/null
+++ b/drivers/delay_timer/generic_delay_timer.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <platform.h>
+
+/* Ticks elapsed in one second by a signal of 1 MHz */
+#define MHZ_TICKS_PER_SEC 1000000
+
+static timer_ops_t ops;
+
+static uint32_t get_timer_value(void)
+{
+ /*
+ * Generic delay timer implementation expects the timer to be a down
+ * counter. We apply bitwise NOT operator to the tick values returned
+ * by read_cntpct_el0() to simulate the down counter. The value is
+ * clipped from 64 to 32 bits.
+ */
+ return (uint32_t)(~read_cntpct_el0());
+}
+
+void generic_delay_timer_init_args(uint32_t mult, uint32_t div)
+{
+ ops.get_timer_value = get_timer_value;
+ ops.clk_mult = mult;
+ ops.clk_div = div;
+
+ timer_init(&ops);
+
+ VERBOSE("Generic delay timer configured with mult=%u and div=%u\n",
+ mult, div);
+}
+
+void generic_delay_timer_init(void)
+{
+ /* Value in ticks */
+ unsigned int mult = MHZ_TICKS_PER_SEC;
+
+ /* Value in ticks per second (Hz) */
+ unsigned int div = plat_get_syscnt_freq2();
+
+ /* Reduce multiplier and divider by dividing them repeatedly by 10 */
+ while ((mult % 10 == 0) && (div % 10 == 0)) {
+ mult /= 10;
+ div /= 10;
+ }
+
+ generic_delay_timer_init_args(mult, div);
+}
+
diff --git a/drivers/emmc/emmc.c b/drivers/emmc/emmc.c
new file mode 100644
index 00000000..5fe28efc
--- /dev/null
+++ b/drivers/emmc/emmc.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Defines a simple and generic interface to access eMMC device.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <emmc.h>
+#include <errno.h>
+#include <string.h>
+
+static const emmc_ops_t *ops;
+static unsigned int emmc_ocr_value;
+static emmc_csd_t emmc_csd;
+
+static int emmc_device_state(void)
+{
+ emmc_cmd_t cmd;
+ int ret;
+
+ do {
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD13;
+ cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+ assert((cmd.resp_data[0] & STATUS_SWITCH_ERROR) == 0);
+ /* Ignore improbable errors in release builds */
+ (void)ret;
+ } while ((cmd.resp_data[0] & STATUS_READY_FOR_DATA) == 0);
+ return EMMC_GET_STATE(cmd.resp_data[0]);
+}
+
+static void emmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
+{
+ emmc_cmd_t cmd;
+ int ret, state;
+
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD6;
+ cmd.cmd_arg = EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
+ EXTCSD_VALUE(value) | 1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ /* wait to exit PRG state */
+ do {
+ state = emmc_device_state();
+ } while (state == EMMC_STATE_PRG);
+ /* Ignore improbable errors in release builds */
+ (void)ret;
+}
+
+static void emmc_set_ios(int clk, int bus_width)
+{
+ int ret;
+
+ /* set IO speed & IO bus width */
+ if (emmc_csd.spec_vers == 4)
+ emmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH, bus_width);
+ ret = ops->set_ios(clk, bus_width);
+ assert(ret == 0);
+ /* Ignore improbable errors in release builds */
+ (void)ret;
+}
+
+static int emmc_enumerate(int clk, int bus_width)
+{
+ emmc_cmd_t cmd;
+ int ret, state;
+
+ ops->init();
+
+ /* CMD0: reset to IDLE */
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD0;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ while (1) {
+ /* CMD1: get OCR register */
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD1;
+ cmd.cmd_arg = OCR_SECTOR_MODE | OCR_VDD_MIN_2V7 |
+ OCR_VDD_MIN_1V7;
+ cmd.resp_type = EMMC_RESPONSE_R3;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+ emmc_ocr_value = cmd.resp_data[0];
+ if (emmc_ocr_value & OCR_POWERUP)
+ break;
+ }
+
+ /* CMD2: Card Identification */
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD2;
+ cmd.resp_type = EMMC_RESPONSE_R2;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ /* CMD3: Set Relative Address */
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD3;
+ cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ /* CMD9: CSD Register */
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD9;
+ cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
+ cmd.resp_type = EMMC_RESPONSE_R2;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+ memcpy(&emmc_csd, &cmd.resp_data, sizeof(cmd.resp_data));
+
+ /* CMD7: Select Card */
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD7;
+ cmd.cmd_arg = EMMC_FIX_RCA << RCA_SHIFT_OFFSET;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+ /* wait to TRAN state */
+ do {
+ state = emmc_device_state();
+ } while (state != EMMC_STATE_TRAN);
+
+ emmc_set_ios(clk, bus_width);
+ return ret;
+}
+
+size_t emmc_read_blocks(int lba, uintptr_t buf, size_t size)
+{
+ emmc_cmd_t cmd;
+ int ret;
+
+ assert((ops != 0) &&
+ (ops->read != 0) &&
+ ((buf & EMMC_BLOCK_MASK) == 0) &&
+ ((size & EMMC_BLOCK_MASK) == 0));
+
+ inv_dcache_range(buf, size);
+ ret = ops->prepare(lba, buf, size);
+ assert(ret == 0);
+
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ if (size > EMMC_BLOCK_SIZE)
+ cmd.cmd_idx = EMMC_CMD18;
+ else
+ cmd.cmd_idx = EMMC_CMD17;
+ if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
+ cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
+ else
+ cmd.cmd_arg = lba;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ ret = ops->read(lba, buf, size);
+ assert(ret == 0);
+
+ /* wait buffer empty */
+ emmc_device_state();
+
+ if (size > EMMC_BLOCK_SIZE) {
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD12;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+ }
+ /* Ignore improbable errors in release builds */
+ (void)ret;
+ return size;
+}
+
+size_t emmc_write_blocks(int lba, const uintptr_t buf, size_t size)
+{
+ emmc_cmd_t cmd;
+ int ret;
+
+ assert((ops != 0) &&
+ (ops->write != 0) &&
+ ((buf & EMMC_BLOCK_MASK) == 0) &&
+ ((size & EMMC_BLOCK_MASK) == 0));
+
+ clean_dcache_range(buf, size);
+ ret = ops->prepare(lba, buf, size);
+ assert(ret == 0);
+
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ if (size > EMMC_BLOCK_SIZE)
+ cmd.cmd_idx = EMMC_CMD25;
+ else
+ cmd.cmd_idx = EMMC_CMD24;
+ if ((emmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE)
+ cmd.cmd_arg = lba * EMMC_BLOCK_SIZE;
+ else
+ cmd.cmd_arg = lba;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ ret = ops->write(lba, buf, size);
+ assert(ret == 0);
+
+ /* wait buffer empty */
+ emmc_device_state();
+
+ if (size > EMMC_BLOCK_SIZE) {
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD12;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+ }
+ /* Ignore improbable errors in release builds */
+ (void)ret;
+ return size;
+}
+
+size_t emmc_erase_blocks(int lba, size_t size)
+{
+ emmc_cmd_t cmd;
+ int ret, state;
+
+ assert(ops != 0);
+ assert((size != 0) && ((size % EMMC_BLOCK_SIZE) == 0));
+
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD35;
+ cmd.cmd_arg = lba;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD36;
+ cmd.cmd_arg = lba + (size / EMMC_BLOCK_SIZE) - 1;
+ cmd.resp_type = EMMC_RESPONSE_R1;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ memset(&cmd, 0, sizeof(emmc_cmd_t));
+ cmd.cmd_idx = EMMC_CMD38;
+ cmd.resp_type = EMMC_RESPONSE_R1B;
+ ret = ops->send_cmd(&cmd);
+ assert(ret == 0);
+
+ /* wait to TRAN state */
+ do {
+ state = emmc_device_state();
+ } while (state != EMMC_STATE_TRAN);
+ /* Ignore improbable errors in release builds */
+ (void)ret;
+ return size;
+}
+
+static inline void emmc_rpmb_enable(void)
+{
+ emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
+ PART_CFG_BOOT_PARTITION1_ENABLE |
+ PART_CFG_PARTITION1_ACCESS);
+}
+
+static inline void emmc_rpmb_disable(void)
+{
+ emmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
+ PART_CFG_BOOT_PARTITION1_ENABLE);
+}
+
+size_t emmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size)
+{
+ size_t size_read;
+
+ emmc_rpmb_enable();
+ size_read = emmc_read_blocks(lba, buf, size);
+ emmc_rpmb_disable();
+ return size_read;
+}
+
+size_t emmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size)
+{
+ size_t size_written;
+
+ emmc_rpmb_enable();
+ size_written = emmc_write_blocks(lba, buf, size);
+ emmc_rpmb_disable();
+ return size_written;
+}
+
+size_t emmc_rpmb_erase_blocks(int lba, size_t size)
+{
+ size_t size_erased;
+
+ emmc_rpmb_enable();
+ size_erased = emmc_erase_blocks(lba, size);
+ emmc_rpmb_disable();
+ return size_erased;
+}
+
+void emmc_init(const emmc_ops_t *ops_ptr, int clk, int width)
+{
+ assert((ops_ptr != 0) &&
+ (ops_ptr->init != 0) &&
+ (ops_ptr->send_cmd != 0) &&
+ (ops_ptr->set_ios != 0) &&
+ (ops_ptr->prepare != 0) &&
+ (ops_ptr->read != 0) &&
+ (ops_ptr->write != 0) &&
+ (clk != 0) &&
+ ((width == EMMC_BUS_WIDTH_1) ||
+ (width == EMMC_BUS_WIDTH_4) ||
+ (width == EMMC_BUS_WIDTH_8)));
+ ops = ops_ptr;
+
+ emmc_enumerate(clk, width);
+}
diff --git a/drivers/gpio/gpio.c b/drivers/gpio/gpio.c
index c06172fc..ef6bb9c8 100644
--- a/drivers/gpio/gpio.c
+++ b/drivers/gpio/gpio.c
@@ -80,6 +80,26 @@ void gpio_set_value(int gpio, int value)
ops->set_value(gpio, value);
}
+void gpio_set_pull(int gpio, int pull)
+{
+ assert(ops);
+ assert(ops->set_pull != 0);
+ assert((pull == GPIO_PULL_NONE) || (pull == GPIO_PULL_UP) ||
+ (pull == GPIO_PULL_DOWN));
+ assert(gpio >= 0);
+
+ ops->set_pull(gpio, pull);
+}
+
+int gpio_get_pull(int gpio)
+{
+ assert(ops);
+ assert(ops->get_pull != 0);
+ assert(gpio >= 0);
+
+ return ops->get_pull(gpio);
+}
+
/*
* Initialize the gpio. The fields in the provided gpio
* ops pointer must be valid.
diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c
new file mode 100644
index 00000000..198b723b
--- /dev/null
+++ b/drivers/io/io_block.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <io_block.h>
+#include <io_driver.h>
+#include <io_storage.h>
+#include <platform_def.h>
+#include <string.h>
+
+typedef struct {
+ io_block_dev_spec_t *dev_spec;
+ uintptr_t base;
+ size_t file_pos;
+ size_t size;
+} block_dev_state_t;
+
+#define is_power_of_2(x) ((x != 0) && ((x & (x - 1)) == 0))
+
+io_type_t device_type_block(void);
+
+static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
+ io_entity_t *entity);
+static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
+static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
+ size_t *length_read);
+static int block_write(io_entity_t *entity, const uintptr_t buffer,
+ size_t length, size_t *length_written);
+static int block_close(io_entity_t *entity);
+static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
+static int block_dev_close(io_dev_info_t *dev_info);
+
+static const io_dev_connector_t block_dev_connector = {
+ .dev_open = block_dev_open
+};
+
+static const io_dev_funcs_t block_dev_funcs = {
+ .type = device_type_block,
+ .open = block_open,
+ .seek = block_seek,
+ .size = NULL,
+ .read = block_read,
+ .write = block_write,
+ .close = block_close,
+ .dev_init = NULL,
+ .dev_close = block_dev_close,
+};
+
+static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
+static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
+
+/* Track number of allocated block state */
+static unsigned int block_dev_count;
+
+io_type_t device_type_block(void)
+{
+ return IO_TYPE_BLOCK;
+}
+
+/* Locate a block state in the pool, specified by address */
+static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
+ unsigned int *index_out)
+{
+ int result = -ENOENT;
+ for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
+ /* dev_spec is used as identifier since it's unique */
+ if (state_pool[index].dev_spec == dev_spec) {
+ result = 0;
+ *index_out = index;
+ break;
+ }
+ }
+ return result;
+}
+
+/* Allocate a device info from the pool and return a pointer to it */
+static int allocate_dev_info(io_dev_info_t **dev_info)
+{
+ int result = -ENOMEM;
+ assert(dev_info != NULL);
+
+ if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
+ unsigned int index = 0;
+ result = find_first_block_state(NULL, &index);
+ assert(result == 0);
+ /* initialize dev_info */
+ dev_info_pool[index].funcs = &block_dev_funcs;
+ dev_info_pool[index].info = (uintptr_t)&state_pool[index];
+ *dev_info = &dev_info_pool[index];
+ ++block_dev_count;
+ }
+
+ return result;
+}
+
+
+/* Release a device info to the pool */
+static int free_dev_info(io_dev_info_t *dev_info)
+{
+ int result;
+ unsigned int index = 0;
+ block_dev_state_t *state;
+ assert(dev_info != NULL);
+
+ state = (block_dev_state_t *)dev_info->info;
+ result = find_first_block_state(state->dev_spec, &index);
+ if (result == 0) {
+ /* free if device info is valid */
+ memset(state, 0, sizeof(block_dev_state_t));
+ memset(dev_info, 0, sizeof(io_dev_info_t));
+ --block_dev_count;
+ }
+
+ return result;
+}
+
+static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
+ io_entity_t *entity)
+{
+ block_dev_state_t *cur;
+ io_block_spec_t *region;
+
+ assert((dev_info->info != (uintptr_t)NULL) &&
+ (spec != (uintptr_t)NULL) &&
+ (entity->info == (uintptr_t)NULL));
+
+ region = (io_block_spec_t *)spec;
+ cur = (block_dev_state_t *)dev_info->info;
+ assert(((region->offset % cur->dev_spec->block_size) == 0) &&
+ ((region->length % cur->dev_spec->block_size) == 0));
+
+ cur->base = region->offset;
+ cur->size = region->length;
+ cur->file_pos = 0;
+
+ entity->info = (uintptr_t)cur;
+ return 0;
+}
+
+/* parameter offset is relative address at here */
+static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
+{
+ block_dev_state_t *cur;
+
+ assert(entity->info != (uintptr_t)NULL);
+
+ cur = (block_dev_state_t *)entity->info;
+ assert((offset >= 0) && (offset < cur->size));
+
+ switch (mode) {
+ case IO_SEEK_SET:
+ cur->file_pos = offset;
+ break;
+ case IO_SEEK_CUR:
+ cur->file_pos += offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ assert(cur->file_pos < cur->size);
+ return 0;
+}
+
+static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
+ size_t *length_read)
+{
+ block_dev_state_t *cur;
+ io_block_spec_t *buf;
+ io_block_ops_t *ops;
+ size_t aligned_length, skip, count, left, padding, block_size;
+ int lba;
+
+ assert(entity->info != (uintptr_t)NULL);
+ cur = (block_dev_state_t *)entity->info;
+ ops = &(cur->dev_spec->ops);
+ buf = &(cur->dev_spec->buffer);
+ block_size = cur->dev_spec->block_size;
+ assert((length <= cur->size) &&
+ (length > 0) &&
+ (ops->read != 0));
+
+ skip = cur->file_pos % block_size;
+ aligned_length = ((skip + length) + (block_size - 1)) &
+ ~(block_size - 1);
+ padding = aligned_length - (skip + length);
+ left = aligned_length;
+ do {
+ lba = (cur->file_pos + cur->base) / block_size;
+ if (left >= buf->length) {
+ /* Since left is larger, it's impossible to padding. */
+ if (skip) {
+ /*
+ * The beginning address (file_pos) isn't
+ * aligned with block size, we need to use
+ * block buffer to read block. Since block
+ * device is always relied on DMA operation.
+ */
+ count = ops->read(lba, buf->offset,
+ buf->length);
+ } else {
+ count = ops->read(lba, buffer, buf->length);
+ }
+ assert(count == buf->length);
+ cur->file_pos += count - skip;
+ if (skip) {
+ /*
+ * Since it's not aligned with block size,
+ * block buffer is used to store data.
+ */
+ memcpy((void *)buffer,
+ (void *)(buf->offset + skip),
+ count - skip);
+ }
+ left = left - (count - skip);
+ } else {
+ if (skip || padding) {
+ /*
+ * The beginning address (file_pos) isn't
+ * aligned with block size, we have to read
+ * full block by block buffer instead.
+ * The size isn't aligned with block size.
+ * Use block buffer to avoid overflow.
+ */
+ count = ops->read(lba, buf->offset, left);
+ } else
+ count = ops->read(lba, buffer, left);
+ assert(count == left);
+ left = left - (skip + padding);
+ cur->file_pos += left;
+ if (skip || padding) {
+ /*
+ * Since it's not aligned with block size,
+ * block buffer is used to store data.
+ */
+ memcpy((void *)buffer,
+ (void *)(buf->offset + skip),
+ left);
+ }
+ /* It's already the last block operation */
+ left = 0;
+ }
+ skip = cur->file_pos % block_size;
+ } while (left > 0);
+ *length_read = length;
+
+ return 0;
+}
+
+static int block_write(io_entity_t *entity, const uintptr_t buffer,
+ size_t length, size_t *length_written)
+{
+ block_dev_state_t *cur;
+ io_block_spec_t *buf;
+ io_block_ops_t *ops;
+ size_t aligned_length, skip, count, left, padding, block_size;
+ int lba;
+
+ assert(entity->info != (uintptr_t)NULL);
+ cur = (block_dev_state_t *)entity->info;
+ ops = &(cur->dev_spec->ops);
+ buf = &(cur->dev_spec->buffer);
+ block_size = cur->dev_spec->block_size;
+ assert((length <= cur->size) &&
+ (length > 0) &&
+ (ops->read != 0) &&
+ (ops->write != 0));
+
+ skip = cur->file_pos % block_size;
+ aligned_length = ((skip + length) + (block_size - 1)) &
+ ~(block_size - 1);
+ padding = aligned_length - (skip + length);
+ left = aligned_length;
+ do {
+ lba = (cur->file_pos + cur->base) / block_size;
+ if (left >= buf->length) {
+ /* Since left is larger, it's impossible to padding. */
+ if (skip) {
+ /*
+ * The beginning address (file_pos) isn't
+ * aligned with block size, we need to use
+ * block buffer to write block. Since block
+ * device is always relied on DMA operation.
+ */
+ count = ops->read(lba, buf->offset,
+ buf->length);
+ assert(count == buf->length);
+ memcpy((void *)(buf->offset + skip),
+ (void *)buffer,
+ count - skip);
+ count = ops->write(lba, buf->offset,
+ buf->length);
+ } else
+ count = ops->write(lba, buffer, buf->length);
+ assert(count == buf->length);
+ cur->file_pos += count - skip;
+ left = left - (count - skip);
+ } else {
+ if (skip || padding) {
+ /*
+ * The beginning address (file_pos) isn't
+ * aligned with block size, we need to avoid
+ * poluate data in the beginning. Reading and
+ * skipping the beginning is the only way.
+ * The size isn't aligned with block size.
+ * Use block buffer to avoid overflow.
+ */
+ count = ops->read(lba, buf->offset, left);
+ assert(count == left);
+ memcpy((void *)(buf->offset + skip),
+ (void *)buffer,
+ left - skip - padding);
+ count = ops->write(lba, buf->offset, left);
+ } else
+ count = ops->write(lba, buffer, left);
+ assert(count == left);
+ cur->file_pos += left - (skip + padding);
+ /* It's already the last block operation */
+ left = 0;
+ }
+ skip = cur->file_pos % block_size;
+ } while (left > 0);
+ *length_written = length;
+ return 0;
+}
+
+static int block_close(io_entity_t *entity)
+{
+ entity->info = (uintptr_t)NULL;
+ return 0;
+}
+
+static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
+{
+ block_dev_state_t *cur;
+ io_block_spec_t *buffer;
+ io_dev_info_t *info;
+ size_t block_size;
+ int result;
+
+ assert(dev_info != NULL);
+ result = allocate_dev_info(&info);
+ if (result)
+ return -ENOENT;
+
+ cur = (block_dev_state_t *)info->info;
+ /* dev_spec is type of io_block_dev_spec_t. */
+ cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
+ buffer = &(cur->dev_spec->buffer);
+ block_size = cur->dev_spec->block_size;
+ assert((block_size > 0) &&
+ (is_power_of_2(block_size) != 0) &&
+ ((buffer->offset % block_size) == 0) &&
+ ((buffer->length % block_size) == 0));
+
+ *dev_info = info; /* cast away const */
+ (void)block_size;
+ (void)buffer;
+ return 0;
+}
+
+static int block_dev_close(io_dev_info_t *dev_info)
+{
+ return free_dev_info(dev_info);
+}
+
+/* Exported functions */
+
+/* Register the Block driver with the IO abstraction */
+int register_io_dev_block(const io_dev_connector_t **dev_con)
+{
+ int result;
+
+ assert(dev_con != NULL);
+
+ /*
+ * Since dev_info isn't really used in io_register_device, always
+ * use the same device info at here instead.
+ */
+ result = io_register_device(&dev_info_pool[0]);
+ if (result == 0)
+ *dev_con = &block_dev_connector;
+ return result;
+}