xref: /rk3399_ARM-atf/drivers/io/io_block.c (revision 9da7a653bda90669057b842b64de4f6b880c8574)
1*9da7a653SHaojian Zhuang /*
2*9da7a653SHaojian Zhuang  * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3*9da7a653SHaojian Zhuang  *
4*9da7a653SHaojian Zhuang  * Redistribution and use in source and binary forms, with or without
5*9da7a653SHaojian Zhuang  * modification, are permitted provided that the following conditions are met:
6*9da7a653SHaojian Zhuang  *
7*9da7a653SHaojian Zhuang  * Redistributions of source code must retain the above copyright notice, this
8*9da7a653SHaojian Zhuang  * list of conditions and the following disclaimer.
9*9da7a653SHaojian Zhuang  *
10*9da7a653SHaojian Zhuang  * Redistributions in binary form must reproduce the above copyright notice,
11*9da7a653SHaojian Zhuang  * this list of conditions and the following disclaimer in the documentation
12*9da7a653SHaojian Zhuang  * and/or other materials provided with the distribution.
13*9da7a653SHaojian Zhuang  *
14*9da7a653SHaojian Zhuang  * Neither the name of ARM nor the names of its contributors may be used
15*9da7a653SHaojian Zhuang  * to endorse or promote products derived from this software without specific
16*9da7a653SHaojian Zhuang  * prior written permission.
17*9da7a653SHaojian Zhuang  *
18*9da7a653SHaojian Zhuang  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19*9da7a653SHaojian Zhuang  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*9da7a653SHaojian Zhuang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*9da7a653SHaojian Zhuang  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22*9da7a653SHaojian Zhuang  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*9da7a653SHaojian Zhuang  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*9da7a653SHaojian Zhuang  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25*9da7a653SHaojian Zhuang  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26*9da7a653SHaojian Zhuang  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27*9da7a653SHaojian Zhuang  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*9da7a653SHaojian Zhuang  * POSSIBILITY OF SUCH DAMAGE.
29*9da7a653SHaojian Zhuang  */
30*9da7a653SHaojian Zhuang 
31*9da7a653SHaojian Zhuang #include <assert.h>
32*9da7a653SHaojian Zhuang #include <debug.h>
33*9da7a653SHaojian Zhuang #include <errno.h>
34*9da7a653SHaojian Zhuang #include <io_block.h>
35*9da7a653SHaojian Zhuang #include <io_driver.h>
36*9da7a653SHaojian Zhuang #include <io_storage.h>
37*9da7a653SHaojian Zhuang #include <platform_def.h>
38*9da7a653SHaojian Zhuang #include <string.h>
39*9da7a653SHaojian Zhuang 
40*9da7a653SHaojian Zhuang typedef struct {
41*9da7a653SHaojian Zhuang 	io_block_dev_spec_t	*dev_spec;
42*9da7a653SHaojian Zhuang 	uintptr_t		base;
43*9da7a653SHaojian Zhuang 	size_t			file_pos;
44*9da7a653SHaojian Zhuang 	size_t			size;
45*9da7a653SHaojian Zhuang } block_dev_state_t;
46*9da7a653SHaojian Zhuang 
47*9da7a653SHaojian Zhuang #define is_power_of_2(x)	((x != 0) && ((x & (x - 1)) == 0))
48*9da7a653SHaojian Zhuang 
49*9da7a653SHaojian Zhuang io_type_t device_type_block(void);
50*9da7a653SHaojian Zhuang 
51*9da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
52*9da7a653SHaojian Zhuang 		      io_entity_t *entity);
53*9da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
54*9da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
55*9da7a653SHaojian Zhuang 		      size_t *length_read);
56*9da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
57*9da7a653SHaojian Zhuang 		       size_t length, size_t *length_written);
58*9da7a653SHaojian Zhuang static int block_close(io_entity_t *entity);
59*9da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
60*9da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info);
61*9da7a653SHaojian Zhuang 
62*9da7a653SHaojian Zhuang static const io_dev_connector_t block_dev_connector = {
63*9da7a653SHaojian Zhuang 	.dev_open	= block_dev_open
64*9da7a653SHaojian Zhuang };
65*9da7a653SHaojian Zhuang 
66*9da7a653SHaojian Zhuang static const io_dev_funcs_t block_dev_funcs = {
67*9da7a653SHaojian Zhuang 	.type		= device_type_block,
68*9da7a653SHaojian Zhuang 	.open		= block_open,
69*9da7a653SHaojian Zhuang 	.seek		= block_seek,
70*9da7a653SHaojian Zhuang 	.size		= NULL,
71*9da7a653SHaojian Zhuang 	.read		= block_read,
72*9da7a653SHaojian Zhuang 	.write		= block_write,
73*9da7a653SHaojian Zhuang 	.close		= block_close,
74*9da7a653SHaojian Zhuang 	.dev_init	= NULL,
75*9da7a653SHaojian Zhuang 	.dev_close	= block_dev_close,
76*9da7a653SHaojian Zhuang };
77*9da7a653SHaojian Zhuang 
78*9da7a653SHaojian Zhuang static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
79*9da7a653SHaojian Zhuang static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
80*9da7a653SHaojian Zhuang 
81*9da7a653SHaojian Zhuang /* Track number of allocated block state */
82*9da7a653SHaojian Zhuang static unsigned int block_dev_count;
83*9da7a653SHaojian Zhuang 
84*9da7a653SHaojian Zhuang io_type_t device_type_block(void)
85*9da7a653SHaojian Zhuang {
86*9da7a653SHaojian Zhuang 	return IO_TYPE_BLOCK;
87*9da7a653SHaojian Zhuang }
88*9da7a653SHaojian Zhuang 
89*9da7a653SHaojian Zhuang /* Locate a block state in the pool, specified by address */
90*9da7a653SHaojian Zhuang static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
91*9da7a653SHaojian Zhuang 				  unsigned int *index_out)
92*9da7a653SHaojian Zhuang {
93*9da7a653SHaojian Zhuang 	int result = -ENOENT;
94*9da7a653SHaojian Zhuang 	for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
95*9da7a653SHaojian Zhuang 		/* dev_spec is used as identifier since it's unique */
96*9da7a653SHaojian Zhuang 		if (state_pool[index].dev_spec == dev_spec) {
97*9da7a653SHaojian Zhuang 			result = 0;
98*9da7a653SHaojian Zhuang 			*index_out = index;
99*9da7a653SHaojian Zhuang 			break;
100*9da7a653SHaojian Zhuang 		}
101*9da7a653SHaojian Zhuang 	}
102*9da7a653SHaojian Zhuang 	return result;
103*9da7a653SHaojian Zhuang }
104*9da7a653SHaojian Zhuang 
105*9da7a653SHaojian Zhuang /* Allocate a device info from the pool and return a pointer to it */
106*9da7a653SHaojian Zhuang static int allocate_dev_info(io_dev_info_t **dev_info)
107*9da7a653SHaojian Zhuang {
108*9da7a653SHaojian Zhuang 	int result = -ENOMEM;
109*9da7a653SHaojian Zhuang 	assert(dev_info != NULL);
110*9da7a653SHaojian Zhuang 
111*9da7a653SHaojian Zhuang 	if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
112*9da7a653SHaojian Zhuang 		unsigned int index = 0;
113*9da7a653SHaojian Zhuang 		result = find_first_block_state(NULL, &index);
114*9da7a653SHaojian Zhuang 		assert(result == 0);
115*9da7a653SHaojian Zhuang 		/* initialize dev_info */
116*9da7a653SHaojian Zhuang 		dev_info_pool[index].funcs = &block_dev_funcs;
117*9da7a653SHaojian Zhuang 		dev_info_pool[index].info = (uintptr_t)&state_pool[index];
118*9da7a653SHaojian Zhuang 		*dev_info = &dev_info_pool[index];
119*9da7a653SHaojian Zhuang 		++block_dev_count;
120*9da7a653SHaojian Zhuang 	}
121*9da7a653SHaojian Zhuang 
122*9da7a653SHaojian Zhuang 	return result;
123*9da7a653SHaojian Zhuang }
124*9da7a653SHaojian Zhuang 
125*9da7a653SHaojian Zhuang 
126*9da7a653SHaojian Zhuang /* Release a device info to the pool */
127*9da7a653SHaojian Zhuang static int free_dev_info(io_dev_info_t *dev_info)
128*9da7a653SHaojian Zhuang {
129*9da7a653SHaojian Zhuang 	int result;
130*9da7a653SHaojian Zhuang 	unsigned int index = 0;
131*9da7a653SHaojian Zhuang 	block_dev_state_t *state;
132*9da7a653SHaojian Zhuang 	assert(dev_info != NULL);
133*9da7a653SHaojian Zhuang 
134*9da7a653SHaojian Zhuang 	state = (block_dev_state_t *)dev_info->info;
135*9da7a653SHaojian Zhuang 	result = find_first_block_state(state->dev_spec, &index);
136*9da7a653SHaojian Zhuang 	if (result ==  0) {
137*9da7a653SHaojian Zhuang 		/* free if device info is valid */
138*9da7a653SHaojian Zhuang 		memset(state, 0, sizeof(block_dev_state_t));
139*9da7a653SHaojian Zhuang 		memset(dev_info, 0, sizeof(io_dev_info_t));
140*9da7a653SHaojian Zhuang 		--block_dev_count;
141*9da7a653SHaojian Zhuang 	}
142*9da7a653SHaojian Zhuang 
143*9da7a653SHaojian Zhuang 	return result;
144*9da7a653SHaojian Zhuang }
145*9da7a653SHaojian Zhuang 
146*9da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
147*9da7a653SHaojian Zhuang 		      io_entity_t *entity)
148*9da7a653SHaojian Zhuang {
149*9da7a653SHaojian Zhuang 	block_dev_state_t *cur;
150*9da7a653SHaojian Zhuang 	io_block_spec_t *region;
151*9da7a653SHaojian Zhuang 
152*9da7a653SHaojian Zhuang 	assert((dev_info->info != (uintptr_t)NULL) &&
153*9da7a653SHaojian Zhuang 	       (spec != (uintptr_t)NULL) &&
154*9da7a653SHaojian Zhuang 	       (entity->info == (uintptr_t)NULL));
155*9da7a653SHaojian Zhuang 
156*9da7a653SHaojian Zhuang 	region = (io_block_spec_t *)spec;
157*9da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)dev_info->info;
158*9da7a653SHaojian Zhuang 	assert(((region->offset % cur->dev_spec->block_size) == 0) &&
159*9da7a653SHaojian Zhuang 	       ((region->length % cur->dev_spec->block_size) == 0));
160*9da7a653SHaojian Zhuang 
161*9da7a653SHaojian Zhuang 	cur->base = region->offset;
162*9da7a653SHaojian Zhuang 	cur->size = region->length;
163*9da7a653SHaojian Zhuang 	cur->file_pos = 0;
164*9da7a653SHaojian Zhuang 
165*9da7a653SHaojian Zhuang 	entity->info = (uintptr_t)cur;
166*9da7a653SHaojian Zhuang 	return 0;
167*9da7a653SHaojian Zhuang }
168*9da7a653SHaojian Zhuang 
169*9da7a653SHaojian Zhuang /* parameter offset is relative address at here */
170*9da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
171*9da7a653SHaojian Zhuang {
172*9da7a653SHaojian Zhuang 	block_dev_state_t *cur;
173*9da7a653SHaojian Zhuang 
174*9da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
175*9da7a653SHaojian Zhuang 
176*9da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
177*9da7a653SHaojian Zhuang 	assert((offset >= 0) && (offset < cur->size));
178*9da7a653SHaojian Zhuang 
179*9da7a653SHaojian Zhuang 	switch (mode) {
180*9da7a653SHaojian Zhuang 	case IO_SEEK_SET:
181*9da7a653SHaojian Zhuang 		cur->file_pos = offset;
182*9da7a653SHaojian Zhuang 		break;
183*9da7a653SHaojian Zhuang 	case IO_SEEK_CUR:
184*9da7a653SHaojian Zhuang 		cur->file_pos += offset;
185*9da7a653SHaojian Zhuang 		break;
186*9da7a653SHaojian Zhuang 	default:
187*9da7a653SHaojian Zhuang 		return -EINVAL;
188*9da7a653SHaojian Zhuang 	}
189*9da7a653SHaojian Zhuang 	assert(cur->file_pos < cur->size);
190*9da7a653SHaojian Zhuang 	return 0;
191*9da7a653SHaojian Zhuang }
192*9da7a653SHaojian Zhuang 
193*9da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
194*9da7a653SHaojian Zhuang 		      size_t *length_read)
195*9da7a653SHaojian Zhuang {
196*9da7a653SHaojian Zhuang 	block_dev_state_t *cur;
197*9da7a653SHaojian Zhuang 	io_block_spec_t *buf;
198*9da7a653SHaojian Zhuang 	io_block_ops_t *ops;
199*9da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
200*9da7a653SHaojian Zhuang 	int lba;
201*9da7a653SHaojian Zhuang 
202*9da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
203*9da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
204*9da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
205*9da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
206*9da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
207*9da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
208*9da7a653SHaojian Zhuang 	       (length > 0) &&
209*9da7a653SHaojian Zhuang 	       (ops->read != 0));
210*9da7a653SHaojian Zhuang 
211*9da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
212*9da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
213*9da7a653SHaojian Zhuang 			 ~(block_size - 1);
214*9da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
215*9da7a653SHaojian Zhuang 	left = aligned_length;
216*9da7a653SHaojian Zhuang 	do {
217*9da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
218*9da7a653SHaojian Zhuang 		if (left >= buf->length) {
219*9da7a653SHaojian Zhuang 			/* Since left is larger, it's impossible to padding. */
220*9da7a653SHaojian Zhuang 			if (skip) {
221*9da7a653SHaojian Zhuang 				/*
222*9da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
223*9da7a653SHaojian Zhuang 				 * aligned with block size, we need to use
224*9da7a653SHaojian Zhuang 				 * block buffer to read block. Since block
225*9da7a653SHaojian Zhuang 				 * device is always relied on DMA operation.
226*9da7a653SHaojian Zhuang 				 */
227*9da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
228*9da7a653SHaojian Zhuang 						  buf->length);
229*9da7a653SHaojian Zhuang 			} else {
230*9da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, buf->length);
231*9da7a653SHaojian Zhuang 			}
232*9da7a653SHaojian Zhuang 			assert(count == buf->length);
233*9da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
234*9da7a653SHaojian Zhuang 			if (skip) {
235*9da7a653SHaojian Zhuang 				/*
236*9da7a653SHaojian Zhuang 				 * Since it's not aligned with block size,
237*9da7a653SHaojian Zhuang 				 * block buffer is used to store data.
238*9da7a653SHaojian Zhuang 				 */
239*9da7a653SHaojian Zhuang 				memcpy((void *)buffer,
240*9da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
241*9da7a653SHaojian Zhuang 				       count - skip);
242*9da7a653SHaojian Zhuang 			}
243*9da7a653SHaojian Zhuang 			left = left - (count - skip);
244*9da7a653SHaojian Zhuang 		} else {
245*9da7a653SHaojian Zhuang 			if (skip || padding) {
246*9da7a653SHaojian Zhuang 				/*
247*9da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
248*9da7a653SHaojian Zhuang 				 * aligned with block size, we have to read
249*9da7a653SHaojian Zhuang 				 * full block by block buffer instead.
250*9da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
251*9da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
252*9da7a653SHaojian Zhuang 				 */
253*9da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
254*9da7a653SHaojian Zhuang 			} else
255*9da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, left);
256*9da7a653SHaojian Zhuang 			assert(count == left);
257*9da7a653SHaojian Zhuang 			left = left - (skip + padding);
258*9da7a653SHaojian Zhuang 			cur->file_pos += left;
259*9da7a653SHaojian Zhuang 			if (skip || padding) {
260*9da7a653SHaojian Zhuang 				/*
261*9da7a653SHaojian Zhuang 				 * Since it's not aligned with block size,
262*9da7a653SHaojian Zhuang 				 * block buffer is used to store data.
263*9da7a653SHaojian Zhuang 				 */
264*9da7a653SHaojian Zhuang 				memcpy((void *)buffer,
265*9da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
266*9da7a653SHaojian Zhuang 				       left);
267*9da7a653SHaojian Zhuang 			}
268*9da7a653SHaojian Zhuang 			/* It's already the last block operation */
269*9da7a653SHaojian Zhuang 			left = 0;
270*9da7a653SHaojian Zhuang 		}
271*9da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
272*9da7a653SHaojian Zhuang 	} while (left > 0);
273*9da7a653SHaojian Zhuang 	*length_read = length;
274*9da7a653SHaojian Zhuang 
275*9da7a653SHaojian Zhuang 	return 0;
276*9da7a653SHaojian Zhuang }
277*9da7a653SHaojian Zhuang 
278*9da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
279*9da7a653SHaojian Zhuang 		       size_t length, size_t *length_written)
280*9da7a653SHaojian Zhuang {
281*9da7a653SHaojian Zhuang 	block_dev_state_t *cur;
282*9da7a653SHaojian Zhuang 	io_block_spec_t *buf;
283*9da7a653SHaojian Zhuang 	io_block_ops_t *ops;
284*9da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
285*9da7a653SHaojian Zhuang 	int lba;
286*9da7a653SHaojian Zhuang 
287*9da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
288*9da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
289*9da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
290*9da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
291*9da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
292*9da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
293*9da7a653SHaojian Zhuang 	       (length > 0) &&
294*9da7a653SHaojian Zhuang 	       (ops->read != 0) &&
295*9da7a653SHaojian Zhuang 	       (ops->write != 0));
296*9da7a653SHaojian Zhuang 
297*9da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
298*9da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
299*9da7a653SHaojian Zhuang 			 ~(block_size - 1);
300*9da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
301*9da7a653SHaojian Zhuang 	left = aligned_length;
302*9da7a653SHaojian Zhuang 	do {
303*9da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
304*9da7a653SHaojian Zhuang 		if (left >= buf->length) {
305*9da7a653SHaojian Zhuang 			/* Since left is larger, it's impossible to padding. */
306*9da7a653SHaojian Zhuang 			if (skip) {
307*9da7a653SHaojian Zhuang 				/*
308*9da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
309*9da7a653SHaojian Zhuang 				 * aligned with block size, we need to use
310*9da7a653SHaojian Zhuang 				 * block buffer to write block. Since block
311*9da7a653SHaojian Zhuang 				 * device is always relied on DMA operation.
312*9da7a653SHaojian Zhuang 				 */
313*9da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
314*9da7a653SHaojian Zhuang 						  buf->length);
315*9da7a653SHaojian Zhuang 				assert(count == buf->length);
316*9da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
317*9da7a653SHaojian Zhuang 				       (void *)buffer,
318*9da7a653SHaojian Zhuang 				       count - skip);
319*9da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset,
320*9da7a653SHaojian Zhuang 						   buf->length);
321*9da7a653SHaojian Zhuang 			} else
322*9da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, buf->length);
323*9da7a653SHaojian Zhuang 			assert(count == buf->length);
324*9da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
325*9da7a653SHaojian Zhuang 			left = left - (count - skip);
326*9da7a653SHaojian Zhuang 		} else {
327*9da7a653SHaojian Zhuang 			if (skip || padding) {
328*9da7a653SHaojian Zhuang 				/*
329*9da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
330*9da7a653SHaojian Zhuang 				 * aligned with block size, we need to avoid
331*9da7a653SHaojian Zhuang 				 * poluate data in the beginning. Reading and
332*9da7a653SHaojian Zhuang 				 * skipping the beginning is the only way.
333*9da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
334*9da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
335*9da7a653SHaojian Zhuang 				 */
336*9da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
337*9da7a653SHaojian Zhuang 				assert(count == left);
338*9da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
339*9da7a653SHaojian Zhuang 				       (void *)buffer,
340*9da7a653SHaojian Zhuang 				       left - skip - padding);
341*9da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset, left);
342*9da7a653SHaojian Zhuang 			} else
343*9da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, left);
344*9da7a653SHaojian Zhuang 			assert(count == left);
345*9da7a653SHaojian Zhuang 			cur->file_pos += left - (skip + padding);
346*9da7a653SHaojian Zhuang 			/* It's already the last block operation */
347*9da7a653SHaojian Zhuang 			left = 0;
348*9da7a653SHaojian Zhuang 		}
349*9da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
350*9da7a653SHaojian Zhuang 	} while (left > 0);
351*9da7a653SHaojian Zhuang 	*length_written = length;
352*9da7a653SHaojian Zhuang 	return 0;
353*9da7a653SHaojian Zhuang }
354*9da7a653SHaojian Zhuang 
355*9da7a653SHaojian Zhuang static int block_close(io_entity_t *entity)
356*9da7a653SHaojian Zhuang {
357*9da7a653SHaojian Zhuang 	entity->info = (uintptr_t)NULL;
358*9da7a653SHaojian Zhuang 	return 0;
359*9da7a653SHaojian Zhuang }
360*9da7a653SHaojian Zhuang 
361*9da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
362*9da7a653SHaojian Zhuang {
363*9da7a653SHaojian Zhuang 	block_dev_state_t *cur;
364*9da7a653SHaojian Zhuang 	io_block_spec_t *buffer;
365*9da7a653SHaojian Zhuang 	io_dev_info_t *info;
366*9da7a653SHaojian Zhuang 	size_t block_size;
367*9da7a653SHaojian Zhuang 	int result;
368*9da7a653SHaojian Zhuang 
369*9da7a653SHaojian Zhuang 	assert(dev_info != NULL);
370*9da7a653SHaojian Zhuang 	result = allocate_dev_info(&info);
371*9da7a653SHaojian Zhuang 	if (result)
372*9da7a653SHaojian Zhuang 		return -ENOENT;
373*9da7a653SHaojian Zhuang 
374*9da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)info->info;
375*9da7a653SHaojian Zhuang 	/* dev_spec is type of io_block_dev_spec_t. */
376*9da7a653SHaojian Zhuang 	cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
377*9da7a653SHaojian Zhuang 	buffer = &(cur->dev_spec->buffer);
378*9da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
379*9da7a653SHaojian Zhuang 	assert((block_size > 0) &&
380*9da7a653SHaojian Zhuang 	       (is_power_of_2(block_size) != 0) &&
381*9da7a653SHaojian Zhuang 	       ((buffer->offset % block_size) == 0) &&
382*9da7a653SHaojian Zhuang 	       ((buffer->length % block_size) == 0));
383*9da7a653SHaojian Zhuang 
384*9da7a653SHaojian Zhuang 	*dev_info = info;	/* cast away const */
385*9da7a653SHaojian Zhuang 	(void)block_size;
386*9da7a653SHaojian Zhuang 	(void)buffer;
387*9da7a653SHaojian Zhuang 	return 0;
388*9da7a653SHaojian Zhuang }
389*9da7a653SHaojian Zhuang 
390*9da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info)
391*9da7a653SHaojian Zhuang {
392*9da7a653SHaojian Zhuang 	return free_dev_info(dev_info);
393*9da7a653SHaojian Zhuang }
394*9da7a653SHaojian Zhuang 
395*9da7a653SHaojian Zhuang /* Exported functions */
396*9da7a653SHaojian Zhuang 
397*9da7a653SHaojian Zhuang /* Register the Block driver with the IO abstraction */
398*9da7a653SHaojian Zhuang int register_io_dev_block(const io_dev_connector_t **dev_con)
399*9da7a653SHaojian Zhuang {
400*9da7a653SHaojian Zhuang 	int result;
401*9da7a653SHaojian Zhuang 
402*9da7a653SHaojian Zhuang 	assert(dev_con != NULL);
403*9da7a653SHaojian Zhuang 
404*9da7a653SHaojian Zhuang 	/*
405*9da7a653SHaojian Zhuang 	 * Since dev_info isn't really used in io_register_device, always
406*9da7a653SHaojian Zhuang 	 * use the same device info at here instead.
407*9da7a653SHaojian Zhuang 	 */
408*9da7a653SHaojian Zhuang 	result = io_register_device(&dev_info_pool[0]);
409*9da7a653SHaojian Zhuang 	if (result == 0)
410*9da7a653SHaojian Zhuang 		*dev_con = &block_dev_connector;
411*9da7a653SHaojian Zhuang 	return result;
412*9da7a653SHaojian Zhuang }
413