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