xref: /rk3399_ARM-atf/drivers/io/io_block.c (revision 9d063aa2e86bc3dc8abf866614607b973358cebb)
19da7a653SHaojian Zhuang /*
29da7a653SHaojian Zhuang  * Copyright (c) 2016, 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>
399da7a653SHaojian Zhuang 
409da7a653SHaojian Zhuang typedef struct {
419da7a653SHaojian Zhuang 	io_block_dev_spec_t	*dev_spec;
429da7a653SHaojian Zhuang 	uintptr_t		base;
439da7a653SHaojian Zhuang 	size_t			file_pos;
449da7a653SHaojian Zhuang 	size_t			size;
459da7a653SHaojian Zhuang } block_dev_state_t;
469da7a653SHaojian Zhuang 
479da7a653SHaojian Zhuang #define is_power_of_2(x)	((x != 0) && ((x & (x - 1)) == 0))
489da7a653SHaojian Zhuang 
499da7a653SHaojian Zhuang io_type_t device_type_block(void);
509da7a653SHaojian Zhuang 
519da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
529da7a653SHaojian Zhuang 		      io_entity_t *entity);
539da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
549da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
559da7a653SHaojian Zhuang 		      size_t *length_read);
569da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
579da7a653SHaojian Zhuang 		       size_t length, size_t *length_written);
589da7a653SHaojian Zhuang static int block_close(io_entity_t *entity);
599da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
609da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info);
619da7a653SHaojian Zhuang 
629da7a653SHaojian Zhuang static const io_dev_connector_t block_dev_connector = {
639da7a653SHaojian Zhuang 	.dev_open	= block_dev_open
649da7a653SHaojian Zhuang };
659da7a653SHaojian Zhuang 
669da7a653SHaojian Zhuang static const io_dev_funcs_t block_dev_funcs = {
679da7a653SHaojian Zhuang 	.type		= device_type_block,
689da7a653SHaojian Zhuang 	.open		= block_open,
699da7a653SHaojian Zhuang 	.seek		= block_seek,
709da7a653SHaojian Zhuang 	.size		= NULL,
719da7a653SHaojian Zhuang 	.read		= block_read,
729da7a653SHaojian Zhuang 	.write		= block_write,
739da7a653SHaojian Zhuang 	.close		= block_close,
749da7a653SHaojian Zhuang 	.dev_init	= NULL,
759da7a653SHaojian Zhuang 	.dev_close	= block_dev_close,
769da7a653SHaojian Zhuang };
779da7a653SHaojian Zhuang 
789da7a653SHaojian Zhuang static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
799da7a653SHaojian Zhuang static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
809da7a653SHaojian Zhuang 
819da7a653SHaojian Zhuang /* Track number of allocated block state */
829da7a653SHaojian Zhuang static unsigned int block_dev_count;
839da7a653SHaojian Zhuang 
849da7a653SHaojian Zhuang io_type_t device_type_block(void)
859da7a653SHaojian Zhuang {
869da7a653SHaojian Zhuang 	return IO_TYPE_BLOCK;
879da7a653SHaojian Zhuang }
889da7a653SHaojian Zhuang 
899da7a653SHaojian Zhuang /* Locate a block state in the pool, specified by address */
909da7a653SHaojian Zhuang static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
919da7a653SHaojian Zhuang 				  unsigned int *index_out)
929da7a653SHaojian Zhuang {
939da7a653SHaojian Zhuang 	int result = -ENOENT;
949da7a653SHaojian Zhuang 	for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
959da7a653SHaojian Zhuang 		/* dev_spec is used as identifier since it's unique */
969da7a653SHaojian Zhuang 		if (state_pool[index].dev_spec == dev_spec) {
979da7a653SHaojian Zhuang 			result = 0;
989da7a653SHaojian Zhuang 			*index_out = index;
999da7a653SHaojian Zhuang 			break;
1009da7a653SHaojian Zhuang 		}
1019da7a653SHaojian Zhuang 	}
1029da7a653SHaojian Zhuang 	return result;
1039da7a653SHaojian Zhuang }
1049da7a653SHaojian Zhuang 
1059da7a653SHaojian Zhuang /* Allocate a device info from the pool and return a pointer to it */
1069da7a653SHaojian Zhuang static int allocate_dev_info(io_dev_info_t **dev_info)
1079da7a653SHaojian Zhuang {
1089da7a653SHaojian Zhuang 	int result = -ENOMEM;
1099da7a653SHaojian Zhuang 	assert(dev_info != NULL);
1109da7a653SHaojian Zhuang 
1119da7a653SHaojian Zhuang 	if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
1129da7a653SHaojian Zhuang 		unsigned int index = 0;
1139da7a653SHaojian Zhuang 		result = find_first_block_state(NULL, &index);
1149da7a653SHaojian Zhuang 		assert(result == 0);
1159da7a653SHaojian Zhuang 		/* initialize dev_info */
1169da7a653SHaojian Zhuang 		dev_info_pool[index].funcs = &block_dev_funcs;
1179da7a653SHaojian Zhuang 		dev_info_pool[index].info = (uintptr_t)&state_pool[index];
1189da7a653SHaojian Zhuang 		*dev_info = &dev_info_pool[index];
1199da7a653SHaojian Zhuang 		++block_dev_count;
1209da7a653SHaojian Zhuang 	}
1219da7a653SHaojian Zhuang 
1229da7a653SHaojian Zhuang 	return result;
1239da7a653SHaojian Zhuang }
1249da7a653SHaojian Zhuang 
1259da7a653SHaojian Zhuang 
1269da7a653SHaojian Zhuang /* Release a device info to the pool */
1279da7a653SHaojian Zhuang static int free_dev_info(io_dev_info_t *dev_info)
1289da7a653SHaojian Zhuang {
1299da7a653SHaojian Zhuang 	int result;
1309da7a653SHaojian Zhuang 	unsigned int index = 0;
1319da7a653SHaojian Zhuang 	block_dev_state_t *state;
1329da7a653SHaojian Zhuang 	assert(dev_info != NULL);
1339da7a653SHaojian Zhuang 
1349da7a653SHaojian Zhuang 	state = (block_dev_state_t *)dev_info->info;
1359da7a653SHaojian Zhuang 	result = find_first_block_state(state->dev_spec, &index);
1369da7a653SHaojian Zhuang 	if (result ==  0) {
1379da7a653SHaojian Zhuang 		/* free if device info is valid */
1389da7a653SHaojian Zhuang 		memset(state, 0, sizeof(block_dev_state_t));
1399da7a653SHaojian Zhuang 		memset(dev_info, 0, sizeof(io_dev_info_t));
1409da7a653SHaojian Zhuang 		--block_dev_count;
1419da7a653SHaojian Zhuang 	}
1429da7a653SHaojian Zhuang 
1439da7a653SHaojian Zhuang 	return result;
1449da7a653SHaojian Zhuang }
1459da7a653SHaojian Zhuang 
1469da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
1479da7a653SHaojian Zhuang 		      io_entity_t *entity)
1489da7a653SHaojian Zhuang {
1499da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1509da7a653SHaojian Zhuang 	io_block_spec_t *region;
1519da7a653SHaojian Zhuang 
1529da7a653SHaojian Zhuang 	assert((dev_info->info != (uintptr_t)NULL) &&
1539da7a653SHaojian Zhuang 	       (spec != (uintptr_t)NULL) &&
1549da7a653SHaojian Zhuang 	       (entity->info == (uintptr_t)NULL));
1559da7a653SHaojian Zhuang 
1569da7a653SHaojian Zhuang 	region = (io_block_spec_t *)spec;
1579da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)dev_info->info;
1589da7a653SHaojian Zhuang 	assert(((region->offset % cur->dev_spec->block_size) == 0) &&
1599da7a653SHaojian Zhuang 	       ((region->length % cur->dev_spec->block_size) == 0));
1609da7a653SHaojian Zhuang 
1619da7a653SHaojian Zhuang 	cur->base = region->offset;
1629da7a653SHaojian Zhuang 	cur->size = region->length;
1639da7a653SHaojian Zhuang 	cur->file_pos = 0;
1649da7a653SHaojian Zhuang 
1659da7a653SHaojian Zhuang 	entity->info = (uintptr_t)cur;
1669da7a653SHaojian Zhuang 	return 0;
1679da7a653SHaojian Zhuang }
1689da7a653SHaojian Zhuang 
1699da7a653SHaojian Zhuang /* parameter offset is relative address at here */
1709da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
1719da7a653SHaojian Zhuang {
1729da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1739da7a653SHaojian Zhuang 
1749da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
1759da7a653SHaojian Zhuang 
1769da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
1779da7a653SHaojian Zhuang 	assert((offset >= 0) && (offset < cur->size));
1789da7a653SHaojian Zhuang 
1799da7a653SHaojian Zhuang 	switch (mode) {
1809da7a653SHaojian Zhuang 	case IO_SEEK_SET:
1819da7a653SHaojian Zhuang 		cur->file_pos = offset;
1829da7a653SHaojian Zhuang 		break;
1839da7a653SHaojian Zhuang 	case IO_SEEK_CUR:
1849da7a653SHaojian Zhuang 		cur->file_pos += offset;
1859da7a653SHaojian Zhuang 		break;
1869da7a653SHaojian Zhuang 	default:
1879da7a653SHaojian Zhuang 		return -EINVAL;
1889da7a653SHaojian Zhuang 	}
1899da7a653SHaojian Zhuang 	assert(cur->file_pos < cur->size);
1909da7a653SHaojian Zhuang 	return 0;
1919da7a653SHaojian Zhuang }
1929da7a653SHaojian Zhuang 
1939da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
1949da7a653SHaojian Zhuang 		      size_t *length_read)
1959da7a653SHaojian Zhuang {
1969da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1979da7a653SHaojian Zhuang 	io_block_spec_t *buf;
1989da7a653SHaojian Zhuang 	io_block_ops_t *ops;
1999da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
2009da7a653SHaojian Zhuang 	int lba;
201*9d063aa2SHaojian Zhuang 	int buffer_not_aligned;
2029da7a653SHaojian Zhuang 
2039da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
2049da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
2059da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
2069da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
2079da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
2089da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
2099da7a653SHaojian Zhuang 	       (length > 0) &&
2109da7a653SHaojian Zhuang 	       (ops->read != 0));
2119da7a653SHaojian Zhuang 
212*9d063aa2SHaojian Zhuang 	if ((buffer & (block_size - 1)) != 0) {
213*9d063aa2SHaojian Zhuang 		/*
214*9d063aa2SHaojian Zhuang 		 * buffer isn't aligned with block size.
215*9d063aa2SHaojian Zhuang 		 * Block device always relies on DMA operation.
216*9d063aa2SHaojian Zhuang 		 * It's better to make the buffer as block size aligned.
217*9d063aa2SHaojian Zhuang 		 */
218*9d063aa2SHaojian Zhuang 		buffer_not_aligned = 1;
219*9d063aa2SHaojian Zhuang 	} else {
220*9d063aa2SHaojian Zhuang 		buffer_not_aligned = 0;
221*9d063aa2SHaojian Zhuang 	}
222*9d063aa2SHaojian Zhuang 
2239da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
2249da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
2259da7a653SHaojian Zhuang 			 ~(block_size - 1);
2269da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
2279da7a653SHaojian Zhuang 	left = aligned_length;
2289da7a653SHaojian Zhuang 	do {
2299da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
2309da7a653SHaojian Zhuang 		if (left >= buf->length) {
231*9d063aa2SHaojian Zhuang 			/*
232*9d063aa2SHaojian Zhuang 			 * Since left is larger, it's impossible to padding.
233*9d063aa2SHaojian Zhuang 			 *
234*9d063aa2SHaojian Zhuang 			 * If buffer isn't aligned, we need to use aligned
235*9d063aa2SHaojian Zhuang 			 * buffer instead.
236*9d063aa2SHaojian Zhuang 			 */
237*9d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
2389da7a653SHaojian Zhuang 				/*
2399da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
2409da7a653SHaojian Zhuang 				 * aligned with block size, we need to use
2419da7a653SHaojian Zhuang 				 * block buffer to read block. Since block
2429da7a653SHaojian Zhuang 				 * device is always relied on DMA operation.
2439da7a653SHaojian Zhuang 				 */
2449da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
2459da7a653SHaojian Zhuang 						  buf->length);
2469da7a653SHaojian Zhuang 			} else {
2479da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, buf->length);
2489da7a653SHaojian Zhuang 			}
2499da7a653SHaojian Zhuang 			assert(count == buf->length);
2509da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
251*9d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
2529da7a653SHaojian Zhuang 				/*
253*9d063aa2SHaojian Zhuang 				 * Since there's not aligned block size caused
254*9d063aa2SHaojian Zhuang 				 * by skip or not aligned buffer, block buffer
255*9d063aa2SHaojian Zhuang 				 * is used to store data.
2569da7a653SHaojian Zhuang 				 */
2579da7a653SHaojian Zhuang 				memcpy((void *)buffer,
2589da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
2599da7a653SHaojian Zhuang 				       count - skip);
2609da7a653SHaojian Zhuang 			}
2619da7a653SHaojian Zhuang 			left = left - (count - skip);
2629da7a653SHaojian Zhuang 		} else {
263*9d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
2649da7a653SHaojian Zhuang 				/*
2659da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
2669da7a653SHaojian Zhuang 				 * aligned with block size, we have to read
2679da7a653SHaojian Zhuang 				 * full block by block buffer instead.
2689da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
2699da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
270*9d063aa2SHaojian Zhuang 				 *
271*9d063aa2SHaojian Zhuang 				 * If buffer isn't aligned, use block buffer
272*9d063aa2SHaojian Zhuang 				 * to avoid DMA error.
2739da7a653SHaojian Zhuang 				 */
2749da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
2759da7a653SHaojian Zhuang 			} else
2769da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, left);
2779da7a653SHaojian Zhuang 			assert(count == left);
2789da7a653SHaojian Zhuang 			left = left - (skip + padding);
2799da7a653SHaojian Zhuang 			cur->file_pos += left;
280*9d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
2819da7a653SHaojian Zhuang 				/*
282*9d063aa2SHaojian Zhuang 				 * Since there's not aligned block size or
283*9d063aa2SHaojian Zhuang 				 * buffer, block buffer is used to store data.
2849da7a653SHaojian Zhuang 				 */
2859da7a653SHaojian Zhuang 				memcpy((void *)buffer,
2869da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
2879da7a653SHaojian Zhuang 				       left);
2889da7a653SHaojian Zhuang 			}
2899da7a653SHaojian Zhuang 			/* It's already the last block operation */
2909da7a653SHaojian Zhuang 			left = 0;
2919da7a653SHaojian Zhuang 		}
2929da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
2939da7a653SHaojian Zhuang 	} while (left > 0);
2949da7a653SHaojian Zhuang 	*length_read = length;
2959da7a653SHaojian Zhuang 
2969da7a653SHaojian Zhuang 	return 0;
2979da7a653SHaojian Zhuang }
2989da7a653SHaojian Zhuang 
2999da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
3009da7a653SHaojian Zhuang 		       size_t length, size_t *length_written)
3019da7a653SHaojian Zhuang {
3029da7a653SHaojian Zhuang 	block_dev_state_t *cur;
3039da7a653SHaojian Zhuang 	io_block_spec_t *buf;
3049da7a653SHaojian Zhuang 	io_block_ops_t *ops;
3059da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
3069da7a653SHaojian Zhuang 	int lba;
307*9d063aa2SHaojian Zhuang 	int buffer_not_aligned;
3089da7a653SHaojian Zhuang 
3099da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
3109da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
3119da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
3129da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
3139da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
3149da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
3159da7a653SHaojian Zhuang 	       (length > 0) &&
3169da7a653SHaojian Zhuang 	       (ops->read != 0) &&
3179da7a653SHaojian Zhuang 	       (ops->write != 0));
3189da7a653SHaojian Zhuang 
319*9d063aa2SHaojian Zhuang 	if ((buffer & (block_size - 1)) != 0) {
320*9d063aa2SHaojian Zhuang 		/*
321*9d063aa2SHaojian Zhuang 		 * buffer isn't aligned with block size.
322*9d063aa2SHaojian Zhuang 		 * Block device always relies on DMA operation.
323*9d063aa2SHaojian Zhuang 		 * It's better to make the buffer as block size aligned.
324*9d063aa2SHaojian Zhuang 		 */
325*9d063aa2SHaojian Zhuang 		buffer_not_aligned = 1;
326*9d063aa2SHaojian Zhuang 	} else {
327*9d063aa2SHaojian Zhuang 		buffer_not_aligned = 0;
328*9d063aa2SHaojian Zhuang 	}
329*9d063aa2SHaojian Zhuang 
3309da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
3319da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
3329da7a653SHaojian Zhuang 			 ~(block_size - 1);
3339da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
3349da7a653SHaojian Zhuang 	left = aligned_length;
3359da7a653SHaojian Zhuang 	do {
3369da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
3379da7a653SHaojian Zhuang 		if (left >= buf->length) {
3389da7a653SHaojian Zhuang 			/* Since left is larger, it's impossible to padding. */
339*9d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
3409da7a653SHaojian Zhuang 				/*
3419da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
342*9d063aa2SHaojian Zhuang 				 * aligned with block size or buffer isn't
343*9d063aa2SHaojian Zhuang 				 * aligned, we need to use block buffer to
344*9d063aa2SHaojian Zhuang 				 * write block.
3459da7a653SHaojian Zhuang 				 */
3469da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
3479da7a653SHaojian Zhuang 						  buf->length);
3489da7a653SHaojian Zhuang 				assert(count == buf->length);
3499da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
3509da7a653SHaojian Zhuang 				       (void *)buffer,
3519da7a653SHaojian Zhuang 				       count - skip);
3529da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset,
3539da7a653SHaojian Zhuang 						   buf->length);
3549da7a653SHaojian Zhuang 			} else
3559da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, buf->length);
3569da7a653SHaojian Zhuang 			assert(count == buf->length);
3579da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
3589da7a653SHaojian Zhuang 			left = left - (count - skip);
3599da7a653SHaojian Zhuang 		} else {
360*9d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
3619da7a653SHaojian Zhuang 				/*
3629da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
3639da7a653SHaojian Zhuang 				 * aligned with block size, we need to avoid
3649da7a653SHaojian Zhuang 				 * poluate data in the beginning. Reading and
3659da7a653SHaojian Zhuang 				 * skipping the beginning is the only way.
3669da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
3679da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
368*9d063aa2SHaojian Zhuang 				 *
369*9d063aa2SHaojian Zhuang 				 * If buffer isn't aligned, use block buffer
370*9d063aa2SHaojian Zhuang 				 * to avoid DMA error.
3719da7a653SHaojian Zhuang 				 */
3729da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
3739da7a653SHaojian Zhuang 				assert(count == left);
3749da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
3759da7a653SHaojian Zhuang 				       (void *)buffer,
3769da7a653SHaojian Zhuang 				       left - skip - padding);
3779da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset, left);
3789da7a653SHaojian Zhuang 			} else
3799da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, left);
3809da7a653SHaojian Zhuang 			assert(count == left);
3819da7a653SHaojian Zhuang 			cur->file_pos += left - (skip + padding);
3829da7a653SHaojian Zhuang 			/* It's already the last block operation */
3839da7a653SHaojian Zhuang 			left = 0;
3849da7a653SHaojian Zhuang 		}
3859da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
3869da7a653SHaojian Zhuang 	} while (left > 0);
3879da7a653SHaojian Zhuang 	*length_written = length;
3889da7a653SHaojian Zhuang 	return 0;
3899da7a653SHaojian Zhuang }
3909da7a653SHaojian Zhuang 
3919da7a653SHaojian Zhuang static int block_close(io_entity_t *entity)
3929da7a653SHaojian Zhuang {
3939da7a653SHaojian Zhuang 	entity->info = (uintptr_t)NULL;
3949da7a653SHaojian Zhuang 	return 0;
3959da7a653SHaojian Zhuang }
3969da7a653SHaojian Zhuang 
3979da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
3989da7a653SHaojian Zhuang {
3999da7a653SHaojian Zhuang 	block_dev_state_t *cur;
4009da7a653SHaojian Zhuang 	io_block_spec_t *buffer;
4019da7a653SHaojian Zhuang 	io_dev_info_t *info;
4029da7a653SHaojian Zhuang 	size_t block_size;
4039da7a653SHaojian Zhuang 	int result;
4049da7a653SHaojian Zhuang 
4059da7a653SHaojian Zhuang 	assert(dev_info != NULL);
4069da7a653SHaojian Zhuang 	result = allocate_dev_info(&info);
4079da7a653SHaojian Zhuang 	if (result)
4089da7a653SHaojian Zhuang 		return -ENOENT;
4099da7a653SHaojian Zhuang 
4109da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)info->info;
4119da7a653SHaojian Zhuang 	/* dev_spec is type of io_block_dev_spec_t. */
4129da7a653SHaojian Zhuang 	cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
4139da7a653SHaojian Zhuang 	buffer = &(cur->dev_spec->buffer);
4149da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
4159da7a653SHaojian Zhuang 	assert((block_size > 0) &&
4169da7a653SHaojian Zhuang 	       (is_power_of_2(block_size) != 0) &&
4179da7a653SHaojian Zhuang 	       ((buffer->offset % block_size) == 0) &&
4189da7a653SHaojian Zhuang 	       ((buffer->length % block_size) == 0));
4199da7a653SHaojian Zhuang 
4209da7a653SHaojian Zhuang 	*dev_info = info;	/* cast away const */
4219da7a653SHaojian Zhuang 	(void)block_size;
4229da7a653SHaojian Zhuang 	(void)buffer;
4239da7a653SHaojian Zhuang 	return 0;
4249da7a653SHaojian Zhuang }
4259da7a653SHaojian Zhuang 
4269da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info)
4279da7a653SHaojian Zhuang {
4289da7a653SHaojian Zhuang 	return free_dev_info(dev_info);
4299da7a653SHaojian Zhuang }
4309da7a653SHaojian Zhuang 
4319da7a653SHaojian Zhuang /* Exported functions */
4329da7a653SHaojian Zhuang 
4339da7a653SHaojian Zhuang /* Register the Block driver with the IO abstraction */
4349da7a653SHaojian Zhuang int register_io_dev_block(const io_dev_connector_t **dev_con)
4359da7a653SHaojian Zhuang {
4369da7a653SHaojian Zhuang 	int result;
4379da7a653SHaojian Zhuang 
4389da7a653SHaojian Zhuang 	assert(dev_con != NULL);
4399da7a653SHaojian Zhuang 
4409da7a653SHaojian Zhuang 	/*
4419da7a653SHaojian Zhuang 	 * Since dev_info isn't really used in io_register_device, always
4429da7a653SHaojian Zhuang 	 * use the same device info at here instead.
4439da7a653SHaojian Zhuang 	 */
4449da7a653SHaojian Zhuang 	result = io_register_device(&dev_info_pool[0]);
4459da7a653SHaojian Zhuang 	if (result == 0)
4469da7a653SHaojian Zhuang 		*dev_con = &block_dev_connector;
4479da7a653SHaojian Zhuang 	return result;
4489da7a653SHaojian Zhuang }
449