Merge pull request #597 from hzhuang1/emmc_v3.2

Emmc v3
This commit is contained in:
danh-arm 2016-04-27 12:31:23 +01:00
commit 32d4f82687
6 changed files with 1002 additions and 0 deletions

View File

@ -448,6 +448,14 @@ must also be defined:
Defines the maximum number of open IO handles. Attempting to open more IO
entities than this value using `io_open()` will fail with -ENOMEM.
* **#define : MAX_IO_BLOCK_DEVICES**
Defines the maximum number of registered IO block devices. Attempting to
register more devices this value using `io_dev_open()` will fail
with -ENOMEM. MAX_IO_BLOCK_DEVICES should be less than MAX_IO_DEVICES.
With this macro, multiple block devices could be supported at the same
time.
If the platform needs to allocate data within the per-cpu data framework in
BL31, it should define the following macro. Currently this is only required if
the platform decides not to use the coherent memory section by undefining the

347
drivers/emmc/emmc.c Normal file
View File

@ -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);
}

412
drivers/io/io_block.c Normal file
View File

@ -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;
}

182
include/drivers/emmc.h Normal file
View File

@ -0,0 +1,182 @@
/*
* 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.
*/
#ifndef __EMMC_H__
#define __EMMC_H__
#include <stdint.h>
#define EMMC_BLOCK_SIZE 512
#define EMMC_BLOCK_MASK (EMMC_BLOCK_SIZE - 1)
#define EMMC_BOOT_CLK_RATE (400 * 1000)
#define EMMC_CMD0 0
#define EMMC_CMD1 1
#define EMMC_CMD2 2
#define EMMC_CMD3 3
#define EMMC_CMD6 6
#define EMMC_CMD7 7
#define EMMC_CMD8 8
#define EMMC_CMD9 9
#define EMMC_CMD12 12
#define EMMC_CMD13 13
#define EMMC_CMD17 17
#define EMMC_CMD18 18
#define EMMC_CMD24 24
#define EMMC_CMD25 25
#define EMMC_CMD35 35
#define EMMC_CMD36 36
#define EMMC_CMD38 38
#define OCR_POWERUP (1 << 31)
#define OCR_BYTE_MODE (0 << 29)
#define OCR_SECTOR_MODE (2 << 29)
#define OCR_ACCESS_MODE_MASK (3 << 29)
#define OCR_VDD_MIN_2V7 (0x1ff << 15)
#define OCR_VDD_MIN_2V0 (0x7f << 8)
#define OCR_VDD_MIN_1V7 (1 << 7)
#define EMMC_RESPONSE_R1 1
#define EMMC_RESPONSE_R1B 1
#define EMMC_RESPONSE_R2 4
#define EMMC_RESPONSE_R3 1
#define EMMC_RESPONSE_R4 1
#define EMMC_RESPONSE_R5 1
#define EMMC_FIX_RCA 6 /* > 1 */
#define RCA_SHIFT_OFFSET 16
#define CMD_EXTCSD_PARTITION_CONFIG 179
#define CMD_EXTCSD_BUS_WIDTH 183
#define CMD_EXTCSD_HS_TIMING 185
#define PART_CFG_BOOT_PARTITION1_ENABLE (1 << 3)
#define PART_CFG_PARTITION1_ACCESS (1 << 0)
/* values in EXT CSD register */
#define EMMC_BUS_WIDTH_1 0
#define EMMC_BUS_WIDTH_4 1
#define EMMC_BUS_WIDTH_8 2
#define EMMC_BOOT_MODE_BACKWARD (0 << 3)
#define EMMC_BOOT_MODE_HS_TIMING (1 << 3)
#define EMMC_BOOT_MODE_DDR (2 << 3)
#define EXTCSD_SET_CMD (0 << 24)
#define EXTCSD_SET_BITS (1 << 24)
#define EXTCSD_CLR_BITS (2 << 24)
#define EXTCSD_WRITE_BYTES (3 << 24)
#define EXTCSD_CMD(x) (((x) & 0xff) << 16)
#define EXTCSD_VALUE(x) (((x) & 0xff) << 8)
#define STATUS_CURRENT_STATE(x) (((x) & 0xf) << 9)
#define STATUS_READY_FOR_DATA (1 << 8)
#define STATUS_SWITCH_ERROR (1 << 7)
#define EMMC_GET_STATE(x) (((x) >> 9) & 0xf)
#define EMMC_STATE_IDLE 0
#define EMMC_STATE_READY 1
#define EMMC_STATE_IDENT 2
#define EMMC_STATE_STBY 3
#define EMMC_STATE_TRAN 4
#define EMMC_STATE_DATA 5
#define EMMC_STATE_RCV 6
#define EMMC_STATE_PRG 7
#define EMMC_STATE_DIS 8
#define EMMC_STATE_BTST 9
#define EMMC_STATE_SLP 10
typedef struct emmc_cmd {
unsigned int cmd_idx;
unsigned int cmd_arg;
unsigned int resp_type;
unsigned int resp_data[4];
} emmc_cmd_t;
typedef struct emmc_ops {
void (*init)(void);
int (*send_cmd)(emmc_cmd_t *cmd);
int (*set_ios)(int clk, int width);
int (*prepare)(int lba, uintptr_t buf, size_t size);
int (*read)(int lba, uintptr_t buf, size_t size);
int (*write)(int lba, const uintptr_t buf, size_t size);
} emmc_ops_t;
typedef struct emmc_csd {
unsigned char not_used: 1;
unsigned char crc: 7;
unsigned char ecc: 2;
unsigned char file_format: 2;
unsigned char tmp_write_protect: 1;
unsigned char perm_write_protect: 1;
unsigned char copy: 1;
unsigned char file_format_grp: 1;
unsigned short reserved_1: 5;
unsigned short write_bl_partial: 1;
unsigned short write_bl_len: 4;
unsigned short r2w_factor: 3;
unsigned short default_ecc: 2;
unsigned short wp_grp_enable: 1;
unsigned int wp_grp_size: 5;
unsigned int erase_grp_mult: 5;
unsigned int erase_grp_size: 5;
unsigned int c_size_mult: 3;
unsigned int vdd_w_curr_max: 3;
unsigned int vdd_w_curr_min: 3;
unsigned int vdd_r_curr_max: 3;
unsigned int vdd_r_curr_min: 3;
unsigned int c_size_low: 2;
unsigned int c_size_high: 10;
unsigned int reserved_2: 2;
unsigned int dsr_imp: 1;
unsigned int read_blk_misalign: 1;
unsigned int write_blk_misalign: 1;
unsigned int read_bl_partial: 1;
unsigned int read_bl_len: 4;
unsigned int ccc: 12;
unsigned int tran_speed: 8;
unsigned int nsac: 8;
unsigned int taac: 8;
unsigned int reserved_3: 2;
unsigned int spec_vers: 4;
unsigned int csd_structure: 2;
} emmc_csd_t;
size_t emmc_read_blocks(int lba, uintptr_t buf, size_t size);
size_t emmc_write_blocks(int lba, const uintptr_t buf, size_t size);
size_t emmc_erase_blocks(int lba, size_t size);
size_t emmc_rpmb_read_blocks(int lba, uintptr_t buf, size_t size);
size_t emmc_rpmb_write_blocks(int lba, const uintptr_t buf, size_t size);
size_t emmc_rpmb_erase_blocks(int lba, size_t size);
void emmc_init(const emmc_ops_t *ops, int clk, int bus_width);
#endif /* __EMMC_H__ */

View File

@ -0,0 +1,52 @@
/*
* 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.
*/
#ifndef __IO_BLOCK_H__
#define __IO_BLOCK_H__
#include <io_storage.h>
/* block devices ops */
typedef struct io_block_ops {
size_t (*read)(int lba, uintptr_t buf, size_t size);
size_t (*write)(int lba, const uintptr_t buf, size_t size);
} io_block_ops_t;
typedef struct io_block_dev_spec {
io_block_spec_t buffer;
io_block_ops_t ops;
size_t block_size;
} io_block_dev_spec_t;
struct io_dev_connector;
int register_io_dev_block(const struct io_dev_connector **dev_con);
#endif /* __IO_BLOCK_H__ */

View File

@ -44,6 +44,7 @@ typedef enum {
IO_TYPE_SEMIHOSTING,
IO_TYPE_MEMMAP,
IO_TYPE_FIRMWARE_IMAGE_PACKAGE,
IO_TYPE_BLOCK,
IO_TYPE_MAX
} io_type_t;