xref: /rk3399_ARM-atf/drivers/io/io_block.c (revision 32f0d3c6c3fb1fb9353ec0b82ddb099281b9328c)
19da7a653SHaojian Zhuang /*
2*32f0d3c6SDouglas Raillard  * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
39da7a653SHaojian Zhuang  *
49da7a653SHaojian Zhuang  * Redistribution and use in source and binary forms, with or without
59da7a653SHaojian Zhuang  * modification, are permitted provided that the following conditions are met:
69da7a653SHaojian Zhuang  *
79da7a653SHaojian Zhuang  * Redistributions of source code must retain the above copyright notice, this
89da7a653SHaojian Zhuang  * list of conditions and the following disclaimer.
99da7a653SHaojian Zhuang  *
109da7a653SHaojian Zhuang  * Redistributions in binary form must reproduce the above copyright notice,
119da7a653SHaojian Zhuang  * this list of conditions and the following disclaimer in the documentation
129da7a653SHaojian Zhuang  * and/or other materials provided with the distribution.
139da7a653SHaojian Zhuang  *
149da7a653SHaojian Zhuang  * Neither the name of ARM nor the names of its contributors may be used
159da7a653SHaojian Zhuang  * to endorse or promote products derived from this software without specific
169da7a653SHaojian Zhuang  * prior written permission.
179da7a653SHaojian Zhuang  *
189da7a653SHaojian Zhuang  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
199da7a653SHaojian Zhuang  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209da7a653SHaojian Zhuang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219da7a653SHaojian Zhuang  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
229da7a653SHaojian Zhuang  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
239da7a653SHaojian Zhuang  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
249da7a653SHaojian Zhuang  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
259da7a653SHaojian Zhuang  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
269da7a653SHaojian Zhuang  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
279da7a653SHaojian Zhuang  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
289da7a653SHaojian Zhuang  * POSSIBILITY OF SUCH DAMAGE.
299da7a653SHaojian Zhuang  */
309da7a653SHaojian Zhuang 
319da7a653SHaojian Zhuang #include <assert.h>
329da7a653SHaojian Zhuang #include <debug.h>
339da7a653SHaojian Zhuang #include <errno.h>
349da7a653SHaojian Zhuang #include <io_block.h>
359da7a653SHaojian Zhuang #include <io_driver.h>
369da7a653SHaojian Zhuang #include <io_storage.h>
379da7a653SHaojian Zhuang #include <platform_def.h>
389da7a653SHaojian Zhuang #include <string.h>
39*32f0d3c6SDouglas Raillard #include <utils.h>
409da7a653SHaojian Zhuang 
419da7a653SHaojian Zhuang typedef struct {
429da7a653SHaojian Zhuang 	io_block_dev_spec_t	*dev_spec;
439da7a653SHaojian Zhuang 	uintptr_t		base;
449da7a653SHaojian Zhuang 	size_t			file_pos;
459da7a653SHaojian Zhuang 	size_t			size;
469da7a653SHaojian Zhuang } block_dev_state_t;
479da7a653SHaojian Zhuang 
489da7a653SHaojian Zhuang #define is_power_of_2(x)	((x != 0) && ((x & (x - 1)) == 0))
499da7a653SHaojian Zhuang 
509da7a653SHaojian Zhuang io_type_t device_type_block(void);
519da7a653SHaojian Zhuang 
529da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
539da7a653SHaojian Zhuang 		      io_entity_t *entity);
549da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
559da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
569da7a653SHaojian Zhuang 		      size_t *length_read);
579da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
589da7a653SHaojian Zhuang 		       size_t length, size_t *length_written);
599da7a653SHaojian Zhuang static int block_close(io_entity_t *entity);
609da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
619da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info);
629da7a653SHaojian Zhuang 
639da7a653SHaojian Zhuang static const io_dev_connector_t block_dev_connector = {
649da7a653SHaojian Zhuang 	.dev_open	= block_dev_open
659da7a653SHaojian Zhuang };
669da7a653SHaojian Zhuang 
679da7a653SHaojian Zhuang static const io_dev_funcs_t block_dev_funcs = {
689da7a653SHaojian Zhuang 	.type		= device_type_block,
699da7a653SHaojian Zhuang 	.open		= block_open,
709da7a653SHaojian Zhuang 	.seek		= block_seek,
719da7a653SHaojian Zhuang 	.size		= NULL,
729da7a653SHaojian Zhuang 	.read		= block_read,
739da7a653SHaojian Zhuang 	.write		= block_write,
749da7a653SHaojian Zhuang 	.close		= block_close,
759da7a653SHaojian Zhuang 	.dev_init	= NULL,
769da7a653SHaojian Zhuang 	.dev_close	= block_dev_close,
779da7a653SHaojian Zhuang };
789da7a653SHaojian Zhuang 
799da7a653SHaojian Zhuang static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
809da7a653SHaojian Zhuang static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
819da7a653SHaojian Zhuang 
829da7a653SHaojian Zhuang /* Track number of allocated block state */
839da7a653SHaojian Zhuang static unsigned int block_dev_count;
849da7a653SHaojian Zhuang 
859da7a653SHaojian Zhuang io_type_t device_type_block(void)
869da7a653SHaojian Zhuang {
879da7a653SHaojian Zhuang 	return IO_TYPE_BLOCK;
889da7a653SHaojian Zhuang }
899da7a653SHaojian Zhuang 
909da7a653SHaojian Zhuang /* Locate a block state in the pool, specified by address */
919da7a653SHaojian Zhuang static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
929da7a653SHaojian Zhuang 				  unsigned int *index_out)
939da7a653SHaojian Zhuang {
949da7a653SHaojian Zhuang 	int result = -ENOENT;
959da7a653SHaojian Zhuang 	for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
969da7a653SHaojian Zhuang 		/* dev_spec is used as identifier since it's unique */
979da7a653SHaojian Zhuang 		if (state_pool[index].dev_spec == dev_spec) {
989da7a653SHaojian Zhuang 			result = 0;
999da7a653SHaojian Zhuang 			*index_out = index;
1009da7a653SHaojian Zhuang 			break;
1019da7a653SHaojian Zhuang 		}
1029da7a653SHaojian Zhuang 	}
1039da7a653SHaojian Zhuang 	return result;
1049da7a653SHaojian Zhuang }
1059da7a653SHaojian Zhuang 
1069da7a653SHaojian Zhuang /* Allocate a device info from the pool and return a pointer to it */
1079da7a653SHaojian Zhuang static int allocate_dev_info(io_dev_info_t **dev_info)
1089da7a653SHaojian Zhuang {
1099da7a653SHaojian Zhuang 	int result = -ENOMEM;
1109da7a653SHaojian Zhuang 	assert(dev_info != NULL);
1119da7a653SHaojian Zhuang 
1129da7a653SHaojian Zhuang 	if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
1139da7a653SHaojian Zhuang 		unsigned int index = 0;
1149da7a653SHaojian Zhuang 		result = find_first_block_state(NULL, &index);
1159da7a653SHaojian Zhuang 		assert(result == 0);
1169da7a653SHaojian Zhuang 		/* initialize dev_info */
1179da7a653SHaojian Zhuang 		dev_info_pool[index].funcs = &block_dev_funcs;
1189da7a653SHaojian Zhuang 		dev_info_pool[index].info = (uintptr_t)&state_pool[index];
1199da7a653SHaojian Zhuang 		*dev_info = &dev_info_pool[index];
1209da7a653SHaojian Zhuang 		++block_dev_count;
1219da7a653SHaojian Zhuang 	}
1229da7a653SHaojian Zhuang 
1239da7a653SHaojian Zhuang 	return result;
1249da7a653SHaojian Zhuang }
1259da7a653SHaojian Zhuang 
1269da7a653SHaojian Zhuang 
1279da7a653SHaojian Zhuang /* Release a device info to the pool */
1289da7a653SHaojian Zhuang static int free_dev_info(io_dev_info_t *dev_info)
1299da7a653SHaojian Zhuang {
1309da7a653SHaojian Zhuang 	int result;
1319da7a653SHaojian Zhuang 	unsigned int index = 0;
1329da7a653SHaojian Zhuang 	block_dev_state_t *state;
1339da7a653SHaojian Zhuang 	assert(dev_info != NULL);
1349da7a653SHaojian Zhuang 
1359da7a653SHaojian Zhuang 	state = (block_dev_state_t *)dev_info->info;
1369da7a653SHaojian Zhuang 	result = find_first_block_state(state->dev_spec, &index);
1379da7a653SHaojian Zhuang 	if (result ==  0) {
1389da7a653SHaojian Zhuang 		/* free if device info is valid */
139*32f0d3c6SDouglas Raillard 		zeromem(state, sizeof(block_dev_state_t));
140*32f0d3c6SDouglas Raillard 		zeromem(dev_info, sizeof(io_dev_info_t));
1419da7a653SHaojian Zhuang 		--block_dev_count;
1429da7a653SHaojian Zhuang 	}
1439da7a653SHaojian Zhuang 
1449da7a653SHaojian Zhuang 	return result;
1459da7a653SHaojian Zhuang }
1469da7a653SHaojian Zhuang 
1479da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
1489da7a653SHaojian Zhuang 		      io_entity_t *entity)
1499da7a653SHaojian Zhuang {
1509da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1519da7a653SHaojian Zhuang 	io_block_spec_t *region;
1529da7a653SHaojian Zhuang 
1539da7a653SHaojian Zhuang 	assert((dev_info->info != (uintptr_t)NULL) &&
1549da7a653SHaojian Zhuang 	       (spec != (uintptr_t)NULL) &&
1559da7a653SHaojian Zhuang 	       (entity->info == (uintptr_t)NULL));
1569da7a653SHaojian Zhuang 
1579da7a653SHaojian Zhuang 	region = (io_block_spec_t *)spec;
1589da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)dev_info->info;
1599da7a653SHaojian Zhuang 	assert(((region->offset % cur->dev_spec->block_size) == 0) &&
1609da7a653SHaojian Zhuang 	       ((region->length % cur->dev_spec->block_size) == 0));
1619da7a653SHaojian Zhuang 
1629da7a653SHaojian Zhuang 	cur->base = region->offset;
1639da7a653SHaojian Zhuang 	cur->size = region->length;
1649da7a653SHaojian Zhuang 	cur->file_pos = 0;
1659da7a653SHaojian Zhuang 
1669da7a653SHaojian Zhuang 	entity->info = (uintptr_t)cur;
1679da7a653SHaojian Zhuang 	return 0;
1689da7a653SHaojian Zhuang }
1699da7a653SHaojian Zhuang 
1709da7a653SHaojian Zhuang /* parameter offset is relative address at here */
1719da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
1729da7a653SHaojian Zhuang {
1739da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1749da7a653SHaojian Zhuang 
1759da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
1769da7a653SHaojian Zhuang 
1779da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
1789da7a653SHaojian Zhuang 	assert((offset >= 0) && (offset < cur->size));
1799da7a653SHaojian Zhuang 
1809da7a653SHaojian Zhuang 	switch (mode) {
1819da7a653SHaojian Zhuang 	case IO_SEEK_SET:
1829da7a653SHaojian Zhuang 		cur->file_pos = offset;
1839da7a653SHaojian Zhuang 		break;
1849da7a653SHaojian Zhuang 	case IO_SEEK_CUR:
1859da7a653SHaojian Zhuang 		cur->file_pos += offset;
1869da7a653SHaojian Zhuang 		break;
1879da7a653SHaojian Zhuang 	default:
1889da7a653SHaojian Zhuang 		return -EINVAL;
1899da7a653SHaojian Zhuang 	}
1909da7a653SHaojian Zhuang 	assert(cur->file_pos < cur->size);
1919da7a653SHaojian Zhuang 	return 0;
1929da7a653SHaojian Zhuang }
1939da7a653SHaojian Zhuang 
1949da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
1959da7a653SHaojian Zhuang 		      size_t *length_read)
1969da7a653SHaojian Zhuang {
1979da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1989da7a653SHaojian Zhuang 	io_block_spec_t *buf;
1999da7a653SHaojian Zhuang 	io_block_ops_t *ops;
2009da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
2019da7a653SHaojian Zhuang 	int lba;
2029d063aa2SHaojian Zhuang 	int buffer_not_aligned;
2039da7a653SHaojian Zhuang 
2049da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
2059da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
2069da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
2079da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
2089da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
2099da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
2109da7a653SHaojian Zhuang 	       (length > 0) &&
2119da7a653SHaojian Zhuang 	       (ops->read != 0));
2129da7a653SHaojian Zhuang 
2139d063aa2SHaojian Zhuang 	if ((buffer & (block_size - 1)) != 0) {
2149d063aa2SHaojian Zhuang 		/*
2159d063aa2SHaojian Zhuang 		 * buffer isn't aligned with block size.
2169d063aa2SHaojian Zhuang 		 * Block device always relies on DMA operation.
2179d063aa2SHaojian Zhuang 		 * It's better to make the buffer as block size aligned.
2189d063aa2SHaojian Zhuang 		 */
2199d063aa2SHaojian Zhuang 		buffer_not_aligned = 1;
2209d063aa2SHaojian Zhuang 	} else {
2219d063aa2SHaojian Zhuang 		buffer_not_aligned = 0;
2229d063aa2SHaojian Zhuang 	}
2239d063aa2SHaojian Zhuang 
2249da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
2259da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
2269da7a653SHaojian Zhuang 			 ~(block_size - 1);
2279da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
2289da7a653SHaojian Zhuang 	left = aligned_length;
2299da7a653SHaojian Zhuang 	do {
2309da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
2319da7a653SHaojian Zhuang 		if (left >= buf->length) {
2329d063aa2SHaojian Zhuang 			/*
2339d063aa2SHaojian Zhuang 			 * Since left is larger, it's impossible to padding.
2349d063aa2SHaojian Zhuang 			 *
2359d063aa2SHaojian Zhuang 			 * If buffer isn't aligned, we need to use aligned
2369d063aa2SHaojian Zhuang 			 * buffer instead.
2379d063aa2SHaojian Zhuang 			 */
2389d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
2399da7a653SHaojian Zhuang 				/*
2409da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
2419da7a653SHaojian Zhuang 				 * aligned with block size, we need to use
2429da7a653SHaojian Zhuang 				 * block buffer to read block. Since block
2439da7a653SHaojian Zhuang 				 * device is always relied on DMA operation.
2449da7a653SHaojian Zhuang 				 */
2459da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
2469da7a653SHaojian Zhuang 						  buf->length);
2479da7a653SHaojian Zhuang 			} else {
2489da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, buf->length);
2499da7a653SHaojian Zhuang 			}
2509da7a653SHaojian Zhuang 			assert(count == buf->length);
2519da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
2529d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
2539da7a653SHaojian Zhuang 				/*
2549d063aa2SHaojian Zhuang 				 * Since there's not aligned block size caused
2559d063aa2SHaojian Zhuang 				 * by skip or not aligned buffer, block buffer
2569d063aa2SHaojian Zhuang 				 * is used to store data.
2579da7a653SHaojian Zhuang 				 */
2589da7a653SHaojian Zhuang 				memcpy((void *)buffer,
2599da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
2609da7a653SHaojian Zhuang 				       count - skip);
2619da7a653SHaojian Zhuang 			}
2629da7a653SHaojian Zhuang 			left = left - (count - skip);
2639da7a653SHaojian Zhuang 		} else {
2649d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
2659da7a653SHaojian Zhuang 				/*
2669da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
2679da7a653SHaojian Zhuang 				 * aligned with block size, we have to read
2689da7a653SHaojian Zhuang 				 * full block by block buffer instead.
2699da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
2709da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
2719d063aa2SHaojian Zhuang 				 *
2729d063aa2SHaojian Zhuang 				 * If buffer isn't aligned, use block buffer
2739d063aa2SHaojian Zhuang 				 * to avoid DMA error.
2749da7a653SHaojian Zhuang 				 */
2759da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
2769da7a653SHaojian Zhuang 			} else
2779da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, left);
2789da7a653SHaojian Zhuang 			assert(count == left);
2799da7a653SHaojian Zhuang 			left = left - (skip + padding);
2809da7a653SHaojian Zhuang 			cur->file_pos += left;
2819d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
2829da7a653SHaojian Zhuang 				/*
2839d063aa2SHaojian Zhuang 				 * Since there's not aligned block size or
2849d063aa2SHaojian Zhuang 				 * buffer, block buffer is used to store data.
2859da7a653SHaojian Zhuang 				 */
2869da7a653SHaojian Zhuang 				memcpy((void *)buffer,
2879da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
2889da7a653SHaojian Zhuang 				       left);
2899da7a653SHaojian Zhuang 			}
2909da7a653SHaojian Zhuang 			/* It's already the last block operation */
2919da7a653SHaojian Zhuang 			left = 0;
2929da7a653SHaojian Zhuang 		}
2939da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
2949da7a653SHaojian Zhuang 	} while (left > 0);
2959da7a653SHaojian Zhuang 	*length_read = length;
2969da7a653SHaojian Zhuang 
2979da7a653SHaojian Zhuang 	return 0;
2989da7a653SHaojian Zhuang }
2999da7a653SHaojian Zhuang 
3009da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
3019da7a653SHaojian Zhuang 		       size_t length, size_t *length_written)
3029da7a653SHaojian Zhuang {
3039da7a653SHaojian Zhuang 	block_dev_state_t *cur;
3049da7a653SHaojian Zhuang 	io_block_spec_t *buf;
3059da7a653SHaojian Zhuang 	io_block_ops_t *ops;
3069da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
3079da7a653SHaojian Zhuang 	int lba;
3089d063aa2SHaojian Zhuang 	int buffer_not_aligned;
3099da7a653SHaojian Zhuang 
3109da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
3119da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
3129da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
3139da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
3149da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
3159da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
3169da7a653SHaojian Zhuang 	       (length > 0) &&
3179da7a653SHaojian Zhuang 	       (ops->read != 0) &&
3189da7a653SHaojian Zhuang 	       (ops->write != 0));
3199da7a653SHaojian Zhuang 
3209d063aa2SHaojian Zhuang 	if ((buffer & (block_size - 1)) != 0) {
3219d063aa2SHaojian Zhuang 		/*
3229d063aa2SHaojian Zhuang 		 * buffer isn't aligned with block size.
3239d063aa2SHaojian Zhuang 		 * Block device always relies on DMA operation.
3249d063aa2SHaojian Zhuang 		 * It's better to make the buffer as block size aligned.
3259d063aa2SHaojian Zhuang 		 */
3269d063aa2SHaojian Zhuang 		buffer_not_aligned = 1;
3279d063aa2SHaojian Zhuang 	} else {
3289d063aa2SHaojian Zhuang 		buffer_not_aligned = 0;
3299d063aa2SHaojian Zhuang 	}
3309d063aa2SHaojian Zhuang 
3319da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
3329da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
3339da7a653SHaojian Zhuang 			 ~(block_size - 1);
3349da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
3359da7a653SHaojian Zhuang 	left = aligned_length;
3369da7a653SHaojian Zhuang 	do {
3379da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
3389da7a653SHaojian Zhuang 		if (left >= buf->length) {
3399da7a653SHaojian Zhuang 			/* Since left is larger, it's impossible to padding. */
3409d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
3419da7a653SHaojian Zhuang 				/*
3429da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
3439d063aa2SHaojian Zhuang 				 * aligned with block size or buffer isn't
3449d063aa2SHaojian Zhuang 				 * aligned, we need to use block buffer to
3459d063aa2SHaojian Zhuang 				 * write block.
3469da7a653SHaojian Zhuang 				 */
3479da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
3489da7a653SHaojian Zhuang 						  buf->length);
3499da7a653SHaojian Zhuang 				assert(count == buf->length);
3509da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
3519da7a653SHaojian Zhuang 				       (void *)buffer,
3529da7a653SHaojian Zhuang 				       count - skip);
3539da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset,
3549da7a653SHaojian Zhuang 						   buf->length);
3559da7a653SHaojian Zhuang 			} else
3569da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, buf->length);
3579da7a653SHaojian Zhuang 			assert(count == buf->length);
3589da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
3599da7a653SHaojian Zhuang 			left = left - (count - skip);
3609da7a653SHaojian Zhuang 		} else {
3619d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
3629da7a653SHaojian Zhuang 				/*
3639da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
3649da7a653SHaojian Zhuang 				 * aligned with block size, we need to avoid
3659da7a653SHaojian Zhuang 				 * poluate data in the beginning. Reading and
3669da7a653SHaojian Zhuang 				 * skipping the beginning is the only way.
3679da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
3689da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
3699d063aa2SHaojian Zhuang 				 *
3709d063aa2SHaojian Zhuang 				 * If buffer isn't aligned, use block buffer
3719d063aa2SHaojian Zhuang 				 * to avoid DMA error.
3729da7a653SHaojian Zhuang 				 */
3739da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
3749da7a653SHaojian Zhuang 				assert(count == left);
3759da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
3769da7a653SHaojian Zhuang 				       (void *)buffer,
3779da7a653SHaojian Zhuang 				       left - skip - padding);
3789da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset, left);
3799da7a653SHaojian Zhuang 			} else
3809da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, left);
3819da7a653SHaojian Zhuang 			assert(count == left);
3829da7a653SHaojian Zhuang 			cur->file_pos += left - (skip + padding);
3839da7a653SHaojian Zhuang 			/* It's already the last block operation */
3849da7a653SHaojian Zhuang 			left = 0;
3859da7a653SHaojian Zhuang 		}
3869da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
3879da7a653SHaojian Zhuang 	} while (left > 0);
3889da7a653SHaojian Zhuang 	*length_written = length;
3899da7a653SHaojian Zhuang 	return 0;
3909da7a653SHaojian Zhuang }
3919da7a653SHaojian Zhuang 
3929da7a653SHaojian Zhuang static int block_close(io_entity_t *entity)
3939da7a653SHaojian Zhuang {
3949da7a653SHaojian Zhuang 	entity->info = (uintptr_t)NULL;
3959da7a653SHaojian Zhuang 	return 0;
3969da7a653SHaojian Zhuang }
3979da7a653SHaojian Zhuang 
3989da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
3999da7a653SHaojian Zhuang {
4009da7a653SHaojian Zhuang 	block_dev_state_t *cur;
4019da7a653SHaojian Zhuang 	io_block_spec_t *buffer;
4029da7a653SHaojian Zhuang 	io_dev_info_t *info;
4039da7a653SHaojian Zhuang 	size_t block_size;
4049da7a653SHaojian Zhuang 	int result;
4059da7a653SHaojian Zhuang 
4069da7a653SHaojian Zhuang 	assert(dev_info != NULL);
4079da7a653SHaojian Zhuang 	result = allocate_dev_info(&info);
4089da7a653SHaojian Zhuang 	if (result)
4099da7a653SHaojian Zhuang 		return -ENOENT;
4109da7a653SHaojian Zhuang 
4119da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)info->info;
4129da7a653SHaojian Zhuang 	/* dev_spec is type of io_block_dev_spec_t. */
4139da7a653SHaojian Zhuang 	cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
4149da7a653SHaojian Zhuang 	buffer = &(cur->dev_spec->buffer);
4159da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
4169da7a653SHaojian Zhuang 	assert((block_size > 0) &&
4179da7a653SHaojian Zhuang 	       (is_power_of_2(block_size) != 0) &&
4189da7a653SHaojian Zhuang 	       ((buffer->offset % block_size) == 0) &&
4199da7a653SHaojian Zhuang 	       ((buffer->length % block_size) == 0));
4209da7a653SHaojian Zhuang 
4219da7a653SHaojian Zhuang 	*dev_info = info;	/* cast away const */
4229da7a653SHaojian Zhuang 	(void)block_size;
4239da7a653SHaojian Zhuang 	(void)buffer;
4249da7a653SHaojian Zhuang 	return 0;
4259da7a653SHaojian Zhuang }
4269da7a653SHaojian Zhuang 
4279da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info)
4289da7a653SHaojian Zhuang {
4299da7a653SHaojian Zhuang 	return free_dev_info(dev_info);
4309da7a653SHaojian Zhuang }
4319da7a653SHaojian Zhuang 
4329da7a653SHaojian Zhuang /* Exported functions */
4339da7a653SHaojian Zhuang 
4349da7a653SHaojian Zhuang /* Register the Block driver with the IO abstraction */
4359da7a653SHaojian Zhuang int register_io_dev_block(const io_dev_connector_t **dev_con)
4369da7a653SHaojian Zhuang {
4379da7a653SHaojian Zhuang 	int result;
4389da7a653SHaojian Zhuang 
4399da7a653SHaojian Zhuang 	assert(dev_con != NULL);
4409da7a653SHaojian Zhuang 
4419da7a653SHaojian Zhuang 	/*
4429da7a653SHaojian Zhuang 	 * Since dev_info isn't really used in io_register_device, always
4439da7a653SHaojian Zhuang 	 * use the same device info at here instead.
4449da7a653SHaojian Zhuang 	 */
4459da7a653SHaojian Zhuang 	result = io_register_device(&dev_info_pool[0]);
4469da7a653SHaojian Zhuang 	if (result == 0)
4479da7a653SHaojian Zhuang 		*dev_con = &block_dev_connector;
4489da7a653SHaojian Zhuang 	return result;
4499da7a653SHaojian Zhuang }
450