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