xref: /rk3399_ARM-atf/drivers/io/io_mtd.c (revision 3a853ad07fde8136ca89dc49a818287052ad67fc)
1 /*
2  * Copyright (c) 2019-2026, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <limits.h>
10 #include <string.h>
11 
12 #include <common/debug.h>
13 #include <drivers/io/io_driver.h>
14 #include <drivers/io/io_mtd.h>
15 #include <lib/utils.h>
16 
17 #include <platform_def.h>
18 
19 typedef struct {
20 	io_mtd_dev_spec_t	*dev_spec;
21 	uintptr_t		base;
22 	unsigned long long	pos;		/* Offset in bytes */
23 	unsigned long long	size;		/* Size of device in bytes */
24 	unsigned long long	extra_offset;	/* Extra offset in bytes */
25 } mtd_dev_state_t;
26 
27 io_type_t device_type_mtd(void);
28 
29 static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec,
30 		    io_entity_t *entity);
31 static int mtd_seek(io_entity_t *entity, int mode, signed long long offset);
32 static int mtd_len(io_entity_t *entity, size_t *length);
33 static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length,
34 		    size_t *length_read);
35 static int mtd_close(io_entity_t *entity);
36 static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
37 static int mtd_dev_close(io_dev_info_t *dev_info);
38 
39 static const io_dev_connector_t mtd_dev_connector = {
40 	.dev_open	= mtd_dev_open
41 };
42 
43 static const io_dev_funcs_t mtd_dev_funcs = {
44 	.type		= device_type_mtd,
45 	.open		= mtd_open,
46 	.seek		= mtd_seek,
47 	.size		= mtd_len,
48 	.read		= mtd_read,
49 	.close		= mtd_close,
50 	.dev_close	= mtd_dev_close,
51 };
52 
53 static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES];
54 static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES];
55 
device_type_mtd(void)56 io_type_t device_type_mtd(void)
57 {
58 	return IO_TYPE_MTD;
59 }
60 
61 /* Locate a MTD state in the pool, specified by address */
find_first_mtd_state(const io_mtd_dev_spec_t * dev_spec,unsigned int * index_out)62 static int find_first_mtd_state(const io_mtd_dev_spec_t *dev_spec,
63 				unsigned int *index_out)
64 {
65 	unsigned int index;
66 	int result = -ENOENT;
67 
68 	for (index = 0U; index < MAX_IO_MTD_DEVICES; index++) {
69 		/* dev_spec is used as identifier since it's unique */
70 		if (state_pool[index].dev_spec == dev_spec) {
71 			result = 0;
72 			*index_out = index;
73 			break;
74 		}
75 	}
76 
77 	return result;
78 }
79 
80 /* Allocate a device info from the pool */
allocate_dev_info(io_dev_info_t ** dev_info)81 static int allocate_dev_info(io_dev_info_t **dev_info)
82 {
83 	unsigned int index = 0U;
84 	int result;
85 
86 	result = find_first_mtd_state(NULL, &index);
87 	if (result != 0) {
88 		return -ENOMEM;
89 	}
90 
91 	dev_info_pool[index].funcs = &mtd_dev_funcs;
92 	dev_info_pool[index].info = (uintptr_t)&state_pool[index];
93 	*dev_info = &dev_info_pool[index];
94 
95 	return 0;
96 }
97 
98 /* Release a device info from the pool */
free_dev_info(io_dev_info_t * dev_info)99 static int free_dev_info(io_dev_info_t *dev_info)
100 {
101 	int result;
102 	unsigned int index = 0U;
103 	mtd_dev_state_t *state;
104 
105 	state = (mtd_dev_state_t *)dev_info->info;
106 	result = find_first_mtd_state(state->dev_spec, &index);
107 	if (result != 0) {
108 		return result;
109 	}
110 
111 	zeromem(state, sizeof(mtd_dev_state_t));
112 	zeromem(dev_info, sizeof(io_dev_info_t));
113 
114 	return 0;
115 }
116 
mtd_add_extra_offset(mtd_dev_state_t * cur,size_t * extra_offset)117 static int mtd_add_extra_offset(mtd_dev_state_t *cur, size_t *extra_offset)
118 {
119 	io_mtd_ops_t *ops = &cur->dev_spec->ops;
120 	int ret;
121 
122 	if (ops->seek == NULL) {
123 		return 0;
124 	}
125 
126 	ret = ops->seek(cur->base, cur->pos, extra_offset);
127 	if (ret != 0) {
128 		ERROR("%s: Seek error %d\n", __func__, ret);
129 		return ret;
130 	}
131 
132 	return 0;
133 }
134 
mtd_open(io_dev_info_t * dev_info,const uintptr_t spec,io_entity_t * entity)135 static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec,
136 		    io_entity_t *entity)
137 {
138 	mtd_dev_state_t *cur;
139 	io_block_spec_t *region;
140 	size_t extra_offset = 0U;
141 	int ret;
142 
143 	assert((dev_info->info != 0UL) && (entity->info == 0UL));
144 
145 	region = (io_block_spec_t *)spec;
146 	cur = (mtd_dev_state_t *)dev_info->info;
147 	entity->info = (uintptr_t)cur;
148 	cur->base = region->offset;
149 	cur->pos = 0U;
150 	cur->extra_offset = 0U;
151 
152 	ret = mtd_add_extra_offset(cur, &extra_offset);
153 	if (ret != 0) {
154 		return ret;
155 	}
156 
157 	cur->base += extra_offset;
158 
159 	return 0;
160 }
161 
162 /* Seek to a specific position using offset */
mtd_seek(io_entity_t * entity,int mode,signed long long offset)163 static int mtd_seek(io_entity_t *entity, int mode, signed long long offset)
164 {
165 	mtd_dev_state_t *cur;
166 	size_t extra_offset = 0U;
167 	int ret;
168 
169 	assert((entity->info != (uintptr_t)NULL) && (offset >= 0));
170 
171 	cur = (mtd_dev_state_t *)entity->info;
172 
173 	switch (mode) {
174 	case IO_SEEK_SET:
175 		if ((offset >= 0) &&
176 		    ((unsigned long long)offset >= cur->size)) {
177 			return -EINVAL;
178 		}
179 
180 		cur->pos = offset;
181 		break;
182 	case IO_SEEK_CUR:
183 		if (((cur->base + cur->pos + (unsigned long long)offset) >=
184 		     cur->size) ||
185 		    ((cur->base + cur->pos + (unsigned long long)offset) <
186 		     cur->base + cur->pos)) {
187 			return -EINVAL;
188 		}
189 
190 		cur->pos += (unsigned long long)offset;
191 		break;
192 	default:
193 		return -EINVAL;
194 	}
195 
196 	ret = mtd_add_extra_offset(cur, &extra_offset);
197 	if (ret != 0) {
198 		return ret;
199 	}
200 
201 	cur->extra_offset = extra_offset;
202 
203 	return 0;
204 }
205 
mtd_len(io_entity_t * entity,size_t * length)206 static int mtd_len(io_entity_t *entity, size_t *length)
207 {
208 	mtd_dev_state_t *cur;
209 
210 	assert(entity->info != (uintptr_t)NULL);
211 	assert(length != NULL);
212 
213 	cur = (mtd_dev_state_t *)entity->info;
214 	if (cur->size > (unsigned long long)SIZE_MAX) {
215 		return -EINVAL;
216 	}
217 
218 	*length = (size_t)cur->size;
219 	return 0;
220 }
221 
mtd_read(io_entity_t * entity,uintptr_t buffer,size_t length,size_t * out_length)222 static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length,
223 		    size_t *out_length)
224 {
225 	mtd_dev_state_t *cur;
226 	io_mtd_ops_t *ops;
227 	int ret;
228 
229 	assert(entity->info != (uintptr_t)NULL);
230 	assert((length > 0U) && (buffer != (uintptr_t)NULL));
231 
232 	cur = (mtd_dev_state_t *)entity->info;
233 	ops = &cur->dev_spec->ops;
234 	assert(ops->read != NULL);
235 
236 	VERBOSE("Read at %llx into %lx, length %zu\n",
237 		cur->base + cur->pos, buffer, length);
238 	if ((cur->base + cur->pos + length) > cur->dev_spec->device_size) {
239 		return -EINVAL;
240 	}
241 
242 	ret = ops->read(cur->base + cur->pos + cur->extra_offset, buffer,
243 			length, out_length);
244 	if (ret < 0) {
245 		return ret;
246 	}
247 
248 	assert(*out_length == length);
249 	cur->pos += *out_length;
250 
251 	return 0;
252 }
253 
mtd_close(io_entity_t * entity)254 static int mtd_close(io_entity_t *entity)
255 {
256 	entity->info = (uintptr_t)NULL;
257 
258 	return 0;
259 }
260 
mtd_dev_open(const uintptr_t dev_spec,io_dev_info_t ** dev_info)261 static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
262 {
263 	mtd_dev_state_t *cur;
264 	io_dev_info_t *info;
265 	io_mtd_ops_t *ops;
266 	int result;
267 
268 	result = allocate_dev_info(&info);
269 	if (result != 0) {
270 		return -ENOENT;
271 	}
272 
273 	cur = (mtd_dev_state_t *)info->info;
274 	cur->dev_spec = (io_mtd_dev_spec_t *)dev_spec;
275 	*dev_info = info;
276 	ops = &(cur->dev_spec->ops);
277 	if (ops->init != NULL) {
278 		result = ops->init(&cur->dev_spec->device_size,
279 				   &cur->dev_spec->erase_size);
280 	}
281 
282 	if (result == 0) {
283 		cur->size = cur->dev_spec->device_size;
284 	} else {
285 		cur->size = 0ULL;
286 	}
287 
288 	return result;
289 }
290 
mtd_dev_close(io_dev_info_t * dev_info)291 static int mtd_dev_close(io_dev_info_t *dev_info)
292 {
293 	return free_dev_info(dev_info);
294 }
295 
296 /* Exported functions */
297 
298 /* Register the MTD driver in the IO abstraction */
register_io_dev_mtd(const io_dev_connector_t ** dev_con)299 int register_io_dev_mtd(const io_dev_connector_t **dev_con)
300 {
301 	int result;
302 
303 	result = io_register_device(&dev_info_pool[0]);
304 	if (result == 0) {
305 		*dev_con = &mtd_dev_connector;
306 	}
307 
308 	return result;
309 }
310