diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/io/vexpress_nor/io_vexpress_nor_hw.c | 433 | ||||
-rw-r--r-- | drivers/io/vexpress_nor/io_vexpress_nor_internal.h | 63 | ||||
-rw-r--r-- | drivers/io/vexpress_nor/io_vexpress_nor_ops.c | 302 |
3 files changed, 798 insertions, 0 deletions
diff --git a/drivers/io/vexpress_nor/io_vexpress_nor_hw.c b/drivers/io/vexpress_nor/io_vexpress_nor_hw.c new file mode 100644 index 0000000..5d3586e --- /dev/null +++ b/drivers/io/vexpress_nor/io_vexpress_nor_hw.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2014, 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 <intel_p30_flash.h> +#include <mmio.h> +#include <string.h> +#include "io_vexpress_nor_internal.h" + +#define MAX_BUFFERED_PROG_ITERATIONS 1000 +#define LOW_16_BITS 0x0000FFFF +#define FOLD_32BIT_INTO_16BIT(value) ((value >> 16) | (value & LOW_16_BITS)) +#define BOUNDARY_OF_32_WORDS 0x7F + +#define CHECK_VPP_RANGE_ERROR(status_register, address) \ + do { \ + if ((status_register) & P30_SR_BIT_VPP) { \ + ERROR("%s (address:0x%X): " \ + "VPP Range Error\n", __func__, address);\ + err = IO_FAIL; \ + } \ + } while (0) + +#define CHECK_BLOCK_LOCK_ERROR(status_register, address) \ + do { \ + if ((status_register) & P30_SR_BIT_BLOCK_LOCKED) { \ + ERROR("%s (address:0x%X): Device Protect " \ + "Error\n", __func__, address); \ + err = IO_FAIL; \ + } \ + } while (0) + +#define CHECK_BLOCK_ERASE_ERROR(status_register, block_offset) \ + do { \ + if ((status_register) & P30_SR_BIT_ERASE) { \ + ERROR("%s (block_offset=0x%08x: " \ + "Block Erase Error status_register" \ + ":0x%x\n", __func__, block_offset, \ + status_register); \ + err = IO_FAIL; \ + } \ + } while (0) + +#define CHECK_SR_BIT_PROGRAM_ERROR(status_register, address) \ + do { \ + if ((status_register) & P30_SR_BIT_PROGRAM) { \ + ERROR("%s(address:0x%X): Program Error\n", \ + __func__, address); \ + err = IO_FAIL; \ + } \ + } while (0) + +static uint32_t flash_read_status(const io_nor_flash_spec_t *device, + uint32_t block_offset) +{ + /* Prepare to read the status register */ + P30_SEND_NOR_COMMAND(device->device_address, 0, + P30_CMD_READ_STATUS_REGISTER); + + return mmio_read_32(device->device_address); +} + +static uint32_t flash_wait_until_complete(const io_nor_flash_spec_t *device, + uint32_t block_offset) +{ + uint32_t lock_status; + + /* Wait until the status register gives us the all clear */ + do { + lock_status = flash_read_status(device, block_offset); + } while ((lock_status & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); + + return lock_status; +} + +static int flash_block_is_locked(uint32_t block_offset) +{ + uint32_t lock_status; + + /* Send command for reading device id */ + P30_SEND_NOR_COMMAND(block_offset, + P30_DEVICE_ID_LOCK_CONFIGURATION, + P30_CMD_READ_DEVICE_ID); + + /* Read block lock status */ + lock_status = mmio_read_32( + P30_CREATE_NOR_ADDRESS(block_offset, + P30_DEVICE_ID_LOCK_CONFIGURATION)); + + /* Decode block lock status */ + lock_status = FOLD_32BIT_INTO_16BIT(lock_status); + + if ((lock_status & P30_DEVICE_ID_BLOCK_LOCKED_DOWN) != 0) + WARN("flash_block_is_locked: Block LOCKED DOWN\n"); + + return lock_status & P30_DEVICE_ID_BLOCK_LOCKED; +} + +static void flash_unlock_block_if_necessary(const io_nor_flash_spec_t *device, + uint32_t block_offset) +{ + if (flash_block_is_locked(block_offset) != 0) { + /* Request a lock setup */ + P30_SEND_NOR_COMMAND(block_offset, 0, P30_CMD_LOCK_BLOCK_SETUP); + + /* Request an unlock */ + P30_SEND_NOR_COMMAND(block_offset, 0, P30_CMD_UNLOCK_BLOCK); + + /* Wait until status register shows device is free */ + flash_wait_until_complete(device, block_offset); + + /* Put device back into Read Array mode */ + P30_SEND_NOR_COMMAND(block_offset, 0, P30_CMD_READ_ARRAY); + } +} + +static int flash_erase_block(const io_nor_flash_spec_t *device, + uint32_t block_offset) +{ + int err = IO_SUCCESS; + uint32_t status_register; + + /* Request a block erase and then confirm it */ + P30_SEND_NOR_COMMAND(block_offset, 0, P30_CMD_BLOCK_ERASE_SETUP); + P30_SEND_NOR_COMMAND(block_offset, 0, P30_CMD_BLOCK_ERASE_CONFIRM); + + /* Wait for the write to complete and then check for any errors; + * i.e. check the Status Register */ + status_register = flash_wait_until_complete(device, block_offset); + + CHECK_VPP_RANGE_ERROR(status_register, block_offset); + + if ((status_register & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == + (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) { + ERROR("flash_erase_block(block_offset=0x%08x: " + "Command Sequence Error\n", block_offset); + err = IO_FAIL; + } + + CHECK_BLOCK_ERASE_ERROR(status_register, block_offset); + + CHECK_BLOCK_LOCK_ERROR(status_register, block_offset); + + if (err) { + /* Clear the Status Register */ + P30_SEND_NOR_COMMAND(device->device_address, 0, + P30_CMD_CLEAR_STATUS_REGISTER); + } + + /* Put device back into Read Array mode */ + P30_SEND_NOR_COMMAND(device->device_address, 0, P30_CMD_READ_ARRAY); + + return err; +} + +/* + * Writes data to the NOR Flash using the Buffered Programming method. + * + * The maximum size of the on-chip buffer is 32-words, because of hardware + * restrictions. Therefore this function will only handle buffers up to 32 + * words or 128 bytes. To deal with larger buffers, call this function again. + * + * This function presumes that both the offset and the offset+BufferSize + * fit entirely within the NOR Flash. Therefore these conditions will not + * be checked here. + * + * In buffered programming, if the target address is not at the beginning of a + * 32-bit word boundary, then programming time is doubled and power consumption + * is increased. Therefore, it is a requirement to align buffer writes to + * 32-bit word boundaries. + */ +static int flash_write_buffer(const io_nor_flash_spec_t *device, + uint32_t offset, + const uint32_t *buffer, + uint32_t buffer_size) +{ + int err = IO_SUCCESS; + uint32_t size_in_words; + uint32_t count; + volatile uint32_t *data; + uint32_t timeout; + int is_buffer_available = 0; + uint32_t status_register; + + timeout = MAX_BUFFERED_PROG_ITERATIONS; + is_buffer_available = 0; + + /* Check that the target offset does not cross a 32-word boundary. */ + if ((offset & BOUNDARY_OF_32_WORDS) != 0) + return IO_FAIL; + + /* This implementation requires the buffer to be 32bit aligned. */ + if (((uintptr_t)buffer & (sizeof(uint32_t) - 1)) != 0) + return IO_FAIL; + + /* Check there are some data to program */ + assert(buffer_size > 0); + + /* Check that the buffer size does not exceed the maximum hardware + * buffer size on chip. + */ + assert(buffer_size <= P30_MAX_BUFFER_SIZE_IN_BYTES); + + /* Check that the buffer size is a multiple of 32-bit words */ + assert((buffer_size % 4) == 0); + + /* Pre-programming conditions checked, now start the algorithm. */ + + /* Prepare the data destination address */ + data = (uint32_t *)(uint64_t)offset; + + /* Check the availability of the buffer */ + do { + /* Issue the Buffered Program Setup command */ + P30_SEND_NOR_COMMAND(offset, 0, P30_CMD_BUFFERED_PROGRAM_SETUP); + + /* Read back the status register bit#7 from the same offset */ + if (((*data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) + is_buffer_available = 1; + + /* Update the loop counter */ + timeout--; + } while ((timeout > 0) && (is_buffer_available == 0)); + + /* The buffer was not available for writing */ + if (timeout == 0) { + err = IO_FAIL; + goto exit; + } + + /* From now on we work in 32-bit words */ + size_in_words = buffer_size / sizeof(uint32_t); + + /* Write the word count, which is (buffer_size_in_words - 1), + * because word count 0 means one word. */ + P30_SEND_NOR_COMMAND(offset, 0, (size_in_words - 1)); + + /* Write the data to the NOR Flash, advancing each address by 4 bytes */ + for (count = 0; count < size_in_words; count++, data++, buffer++) + *data = *buffer; + + /* Issue the Buffered Program Confirm command, to start the programming + * operation */ + P30_SEND_NOR_COMMAND(device->device_address, 0, + P30_CMD_BUFFERED_PROGRAM_CONFIRM); + + /* Wait for the write to complete and then check for any errors; + * i.e. check the Status Register */ + status_register = flash_wait_until_complete(device, offset); + + /* Perform a full status check: + * Mask the relevant bits of Status Register. + * Everything should be zero, if not, we have a problem */ + + CHECK_VPP_RANGE_ERROR(status_register, offset); + + CHECK_SR_BIT_PROGRAM_ERROR(status_register, offset); + + CHECK_BLOCK_LOCK_ERROR(status_register, offset); + + if (err != IO_SUCCESS) { + /* Clear the Status Register */ + P30_SEND_NOR_COMMAND(device->device_address, 0, + P30_CMD_CLEAR_STATUS_REGISTER); + } + +exit: + /* Put device back into Read Array mode */ + P30_SEND_NOR_COMMAND(device->device_address, 0, P30_CMD_READ_ARRAY); + + return err; +} + +static int flash_write_single_word(const io_nor_flash_spec_t *device, + int32_t offset, uint32_t data) +{ + int err = IO_SUCCESS; + uint32_t status_register; + + /* NOR Flash Programming: Request a write single word command */ + P30_SEND_NOR_COMMAND(offset, 0, P30_CMD_WORD_PROGRAM_SETUP); + + /* Store the word into NOR Flash; */ + mmio_write_32(offset, data); + + /* Wait for the write to complete and then check for any errors; + * i.e. check the Status Register */ + status_register = flash_wait_until_complete(device, offset); + + /* Perform a full status check: */ + /* Mask the relevant bits of Status Register. + * Everything should be zero, if not, we have a problem */ + + CHECK_VPP_RANGE_ERROR(status_register, offset); + + CHECK_SR_BIT_PROGRAM_ERROR(status_register, offset); + + CHECK_BLOCK_LOCK_ERROR(status_register, offset); + + if (err != IO_SUCCESS) + /* Clear the Status Register */ + P30_SEND_NOR_COMMAND(device->device_address, 0, + P30_CMD_CLEAR_STATUS_REGISTER); + + /* Put device back into Read Array mode */ + P30_SEND_NOR_COMMAND(device->device_address, 0, P30_CMD_READ_ARRAY); + + return err; +} + +int flash_block_write(file_state_t *fp, uint32_t offset, + const uintptr_t buffer, size_t *written) +{ + int ret = IO_SUCCESS; + uintptr_t buffer_ptr = buffer; + uint32_t buffer_size; + uint32_t remaining = fp->block_spec->block_size; + uint32_t flash_pos = fp->block_spec->region_address + offset; + + /* address passed should be block aligned */ + assert(!(offset % fp->block_spec->block_size)); + + /* Unlock block */ + flash_unlock_block_if_necessary(fp->block_spec, flash_pos); + + /* Erase one block */ + flash_erase_block(fp->block_spec, flash_pos); + + /* Start by using NOR Flash buffer while the buffer size is a multiple + * of 32-bit */ + while ((remaining >= sizeof(uint32_t)) && (ret == IO_SUCCESS)) { + if (remaining >= P30_MAX_BUFFER_SIZE_IN_BYTES) + buffer_size = P30_MAX_BUFFER_SIZE_IN_BYTES; + else + /* Copy the remaining 32bit words of the buffer */ + buffer_size = remaining & (sizeof(uint32_t) - 1); + + ret = flash_write_buffer(fp->block_spec, flash_pos, + (const uint32_t *)buffer_ptr, buffer_size); + flash_pos += buffer_size; + remaining -= buffer_size; + buffer_ptr += buffer_size; + + } + + /* Write the remaining bytes */ + while ((remaining > 0) && (ret == IO_SUCCESS)) { + ret = flash_write_single_word(fp->block_spec, + flash_pos++, buffer_ptr++); + remaining--; + } + + if (ret == IO_SUCCESS) + *written = fp->block_spec->block_size; + + return ret; +} + +/* In case of partial write we need to save the block into a temporary buffer */ +static char block_buffer[NOR_FLASH_BLOCK_SIZE_MAX]; + +int flash_partial_write(file_state_t *fp, uint32_t offset, + const uintptr_t buffer, size_t length, size_t *written) +{ + uintptr_t block_start; + uint32_t block_size; + uint32_t block_offset; + int ret; + + assert((fp != NULL) && (fp->block_spec != NULL)); + assert(written != NULL); + + block_size = fp->block_spec->block_size; + /* Start address of the block to write */ + block_start = (offset / block_size) * block_size; + + /* Ensure 'block_buffer' is big enough to contain a copy of the block. + * 'block_buffer' is reserved at build time - so it might not match */ + assert(sizeof(block_buffer) >= block_size); + + /* + * Check the buffer fits inside a single block. + * It must not span several blocks + */ + assert((offset / block_size) == + ((offset + length - 1) / block_size)); + + /* Make a copy of the block from flash to a temporary buffer */ + memcpy(block_buffer, (void *)(fp->block_spec->region_address + + block_start), block_size); + + /* Calculate the offset of the buffer into the block */ + block_offset = offset % block_size; + + /* update the content of the block buffer */ + memcpy(block_buffer + block_offset, (void *)buffer, length); + + /* Write the block buffer back */ + ret = flash_block_write(fp, block_start, + (uintptr_t)block_buffer, written); + if (ret == IO_SUCCESS) + *written = length; + + return ret; +} diff --git a/drivers/io/vexpress_nor/io_vexpress_nor_internal.h b/drivers/io/vexpress_nor/io_vexpress_nor_internal.h new file mode 100644 index 0000000..c75215b --- /dev/null +++ b/drivers/io/vexpress_nor/io_vexpress_nor_internal.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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. + */ + +#ifndef __IO_VEXPRESS_NOR_INTERNAL_H__ +#define __IO_VEXPRESS_NOR_INTERNAL_H__ + +#include <io_driver.h> +#include <io_nor_flash.h> +#include <io_storage.h> + +#define IS_FLASH_ADDRESS_BLOCK_ALIGNED(fp, addr) \ + (((addr) & ((fp)->block_spec->block_size - 1)) == 0) + +/* As we need to be able to keep state for seek, only one file can be open + * at a time. Make this a structure and point to the entity->info. When we + * can malloc memory we can change this to support more open files. + */ +typedef struct { + /* Use the 'in_use' flag as any value for base and file_pos could be + * valid. + */ + int in_use; + uintptr_t base; + size_t file_pos; + + /* Definition of the flash block device */ + const io_nor_flash_spec_t *block_spec; +} file_state_t; + +int flash_block_write(file_state_t *fp, uint32_t address, + const uintptr_t buffer, size_t *written); + +int flash_partial_write(file_state_t *fp, uint32_t address, + const uintptr_t buffer, size_t length, size_t *written); + +#endif /* __IO_VEXPRESS_NOR_INTERNAL_H__ */ diff --git a/drivers/io/vexpress_nor/io_vexpress_nor_ops.c b/drivers/io/vexpress_nor/io_vexpress_nor_ops.c new file mode 100644 index 0000000..c9de422 --- /dev/null +++ b/drivers/io/vexpress_nor/io_vexpress_nor_ops.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2014, 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 <string.h> +#include "io_vexpress_nor_internal.h" + +static file_state_t current_file = {0}; + +/* Identify the device type as flash */ +io_type_t device_type_flash(void) +{ + return IO_TYPE_FLASH; +} + +/* NOR Flash device functions */ +static int flash_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); +static int flash_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int flash_seek(io_entity_t *entity, int mode, + ssize_t offset); +static int flash_read(io_entity_t *entity, uintptr_t buffer, + size_t length, size_t *length_read); +static int flash_write(io_entity_t *entity, const uintptr_t buffer, + size_t length, size_t *length_written); +static int flash_close(io_entity_t *entity); +static int flash_dev_close(io_dev_info_t *dev_info); + + +static const io_dev_connector_t flash_dev_connector = { + .dev_open = flash_dev_open +}; + +static const io_dev_funcs_t flash_dev_funcs = { + .type = device_type_flash, + .open = flash_open, + .seek = flash_seek, + .size = NULL, + .read = flash_read, + .write = flash_write, + .close = flash_close, + .dev_init = NULL, + .dev_close = flash_dev_close, +}; + +/* No state associated with this device so structure can be const */ +static const io_dev_info_t flash_dev_info = { + .funcs = &flash_dev_funcs, + .info = (uintptr_t)NULL +}; + +/* Open a connection to the flash device */ +static int flash_dev_open(const uintptr_t dev_spec __attribute__((unused)), + io_dev_info_t **dev_info) +{ + assert(dev_info != NULL); + *dev_info = (io_dev_info_t *)&flash_dev_info; /* cast away const */ + + return IO_SUCCESS; +} + +/* Close a connection to the flash device */ +static int flash_dev_close(io_dev_info_t *dev_info) +{ + /* NOP */ + /* TODO: Consider tracking open files and cleaning them up here */ + return IO_SUCCESS; +} + + +/* Open a file on the flash device */ +/* TODO: Can we do any sensible limit checks on requested memory */ +static int flash_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity) +{ + int result; + const io_nor_flash_spec_t *block_spec = (io_nor_flash_spec_t *)spec; + + /* Since we need to track open state for seek() we only allow one open + * spec at a time. When we have dynamic memory we can malloc and set + * entity->info. + */ + if (current_file.in_use == 0) { + assert(block_spec != NULL); + assert(entity != NULL); + + current_file.in_use = 1; + current_file.base = block_spec->region_address; + /* File cursor offset for seek and incremental reads etc. */ + current_file.file_pos = 0; + /* Attach the device specification to this file */ + current_file.block_spec = block_spec; + + entity->info = (uintptr_t)¤t_file; + result = IO_SUCCESS; + } else { + WARN("A Flash device is already active. Close first.\n"); + result = IO_RESOURCES_EXHAUSTED; + } + + return result; +} + +/* Seek to a particular file offset on the flash device */ +static int flash_seek(io_entity_t *entity, int mode, ssize_t offset) +{ + const io_nor_flash_spec_t *block_spec; + int result; + uintptr_t flash_base; + size_t flash_size; + + assert(entity != NULL); + + block_spec = ((file_state_t *)entity->info)->block_spec; + flash_size = block_spec->block_count * block_spec->block_size; + + if (mode == IO_SEEK_SET) { + /* Ensure the offset is into the flash */ + if ((offset >= 0) && (offset < flash_size)) { + ((file_state_t *)entity->info)->file_pos = offset; + result = IO_SUCCESS; + } else { + result = IO_FAIL; + } + } else if (mode == IO_SEEK_END) { + flash_base = block_spec->region_address; + /* Ensure the offset is into the flash */ + if ((offset <= 0) && (flash_size + offset >= 0)) { + ((file_state_t *)entity->info)->file_pos = + flash_base + flash_size + offset; + result = IO_SUCCESS; + } else { + result = IO_FAIL; + } + } else if (mode == IO_SEEK_CUR) { + flash_base = block_spec->region_address; + /* Ensure the offset is into the flash */ + if ((((file_state_t *)entity->info)->file_pos + + offset >= flash_base) && + (((file_state_t *)entity->info)->file_pos + + offset < flash_base + flash_size)) { + ((file_state_t *)entity->info)->file_pos += offset; + result = IO_SUCCESS; + } else { + result = IO_FAIL; + } + } else { + result = IO_FAIL; + } + + return result; +} + +/* Read data from a file on the flash device */ +static int flash_read(io_entity_t *entity, uintptr_t buffer, + size_t length, size_t *length_read) +{ + file_state_t *fp; + + assert(entity != NULL); + assert(buffer != (uintptr_t)NULL); + assert(length_read != NULL); + + fp = (file_state_t *)entity->info; + + memcpy((void *)buffer, (void *)(fp->base + fp->file_pos), length); + + *length_read = length; + /* advance the file 'cursor' for incremental reads */ + fp->file_pos += length; + + return IO_SUCCESS; +} + +/* Write data to a file on the flash device */ +static int flash_write(io_entity_t *entity, const uintptr_t buffer, + size_t length, size_t *length_written) +{ + file_state_t *fp; + const io_nor_flash_spec_t *block_spec; + size_t file_pos; + int ret; + uint32_t remaining_block_size; + size_t written; + uintptr_t buffer_ptr = buffer; + + assert(entity != NULL); + assert(buffer != (uintptr_t)NULL); + assert(length_written != NULL); + + fp = (file_state_t *)entity->info; + block_spec = fp->block_spec; + file_pos = fp->file_pos; + + *length_written = 0; + + while (length > 0) { + /* Check if we can do a block write */ + if (IS_FLASH_ADDRESS_BLOCK_ALIGNED(fp, file_pos)) { + if (length / block_spec->block_size > 0) { + ret = flash_block_write(fp, file_pos, + buffer_ptr, &written); + } else { + /* Case when the length is smaller than a + * block size + */ + ret = flash_partial_write(fp, file_pos, + buffer_ptr, length, &written); + } + } else { + /* Case when the buffer does not start at the beginning + * of a block + */ + + /* Length between the current file_pos and the end of + * the block + */ + remaining_block_size = block_spec->block_size - + (file_pos % block_spec->block_size); + + if (length < remaining_block_size) { + ret = flash_partial_write(fp, file_pos, + buffer_ptr, length, &written); + } else { + ret = flash_partial_write(fp, file_pos, + buffer_ptr, remaining_block_size, + &written); + } + } + + /* If one of the write operations fails then we do not pursue */ + if (ret != IO_SUCCESS) { + return ret; + } else { + buffer_ptr += written; + file_pos += written; + + *length_written += written; + length -= written; + } + } + + /* advance the file 'cursor' for incremental writes */ + fp->file_pos += length; + + return IO_SUCCESS; +} + +/* Close a file on the flash device */ +static int flash_close(io_entity_t *entity) +{ + assert(entity != NULL); + + entity->info = 0; + + /* This would be a mem free() if we had malloc.*/ + memset((void *)¤t_file, 0, sizeof(current_file)); + + return IO_SUCCESS; +} + +/* Exported functions */ + +/* Register the flash driver with the IO abstraction */ +int register_io_dev_nor_flash(const io_dev_connector_t **dev_con) +{ + int result; + assert(dev_con != NULL); + + result = io_register_device(&flash_dev_info); + if (result == IO_SUCCESS) + *dev_con = &flash_dev_connector; + + return result; +} |