blob: 5d86592db9b4cb5a4c8c9fae355839f21b5da988 [file] [log] [blame]
Lionel Debieve64a524d2019-09-09 20:13:34 +02001/*
Yann Gautier442b2232022-02-14 09:56:54 +01002 * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved.
Lionel Debieve64a524d2019-09-09 20:13:34 +02003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <errno.h>
9#include <string.h>
10
Lionel Debieve64a524d2019-09-09 20:13:34 +020011#include <common/debug.h>
12#include <drivers/io/io_driver.h>
13#include <drivers/io/io_mtd.h>
14#include <lib/utils.h>
15
Yann Gautier442b2232022-02-14 09:56:54 +010016#include <platform_def.h>
17
Lionel Debieve64a524d2019-09-09 20:13:34 +020018typedef struct {
19 io_mtd_dev_spec_t *dev_spec;
20 uintptr_t base;
Lionel Debieve02a48072020-07-17 12:35:30 +020021 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 */
Lionel Debieve64a524d2019-09-09 20:13:34 +020024} mtd_dev_state_t;
25
26io_type_t device_type_mtd(void);
27
28static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec,
29 io_entity_t *entity);
30static int mtd_seek(io_entity_t *entity, int mode, signed long long offset);
31static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length,
32 size_t *length_read);
33static int mtd_close(io_entity_t *entity);
34static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
35static int mtd_dev_close(io_dev_info_t *dev_info);
36
37static const io_dev_connector_t mtd_dev_connector = {
38 .dev_open = mtd_dev_open
39};
40
41static 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
50static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES];
51static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES];
52
53io_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 */
59static 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 */
78static 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 */
96static 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
Lionel Debieve02a48072020-07-17 12:35:30 +0200114static 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
Lionel Debieve64a524d2019-09-09 20:13:34 +0200132static 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;
Lionel Debieve02a48072020-07-17 12:35:30 +0200136 io_block_spec_t *region;
137 size_t extra_offset = 0U;
138 int ret;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200139
140 assert((dev_info->info != 0UL) && (entity->info == 0UL));
141
Lionel Debieve02a48072020-07-17 12:35:30 +0200142 region = (io_block_spec_t *)spec;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200143 cur = (mtd_dev_state_t *)dev_info->info;
144 entity->info = (uintptr_t)cur;
Lionel Debieve02a48072020-07-17 12:35:30 +0200145 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;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200155
156 return 0;
157}
158
159/* Seek to a specific position using offset */
160static int mtd_seek(io_entity_t *entity, int mode, signed long long offset)
161{
162 mtd_dev_state_t *cur;
Lionel Debieve02a48072020-07-17 12:35:30 +0200163 size_t extra_offset = 0U;
164 int ret;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200165
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
Lionel Debieve02a48072020-07-17 12:35:30 +0200177 cur->pos = offset;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200178 break;
179 case IO_SEEK_CUR:
Lionel Debieve02a48072020-07-17 12:35:30 +0200180 if (((cur->base + cur->pos + (unsigned long long)offset) >=
Lionel Debieve64a524d2019-09-09 20:13:34 +0200181 cur->size) ||
Lionel Debieve02a48072020-07-17 12:35:30 +0200182 ((cur->base + cur->pos + (unsigned long long)offset) <
183 cur->base + cur->pos)) {
Lionel Debieve64a524d2019-09-09 20:13:34 +0200184 return -EINVAL;
185 }
186
Lionel Debieve02a48072020-07-17 12:35:30 +0200187 cur->pos += (unsigned long long)offset;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200188 break;
189 default:
190 return -EINVAL;
191 }
192
Lionel Debieve02a48072020-07-17 12:35:30 +0200193 ret = mtd_add_extra_offset(cur, &extra_offset);
194 if (ret != 0) {
195 return ret;
196 }
197
198 cur->extra_offset = extra_offset;
199
Lionel Debieve64a524d2019-09-09 20:13:34 +0200200 return 0;
201}
202
203static 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
Yann Gautier442b2232022-02-14 09:56:54 +0100217 VERBOSE("Read at %llx into %lx, length %zu\n",
Lionel Debieve02a48072020-07-17 12:35:30 +0200218 cur->base + cur->pos, buffer, length);
219 if ((cur->base + cur->pos + length) > cur->dev_spec->device_size) {
Lionel Debieve64a524d2019-09-09 20:13:34 +0200220 return -EINVAL;
221 }
222
Lionel Debieve02a48072020-07-17 12:35:30 +0200223 ret = ops->read(cur->base + cur->pos + cur->extra_offset, buffer,
224 length, out_length);
Lionel Debieve64a524d2019-09-09 20:13:34 +0200225 if (ret < 0) {
226 return ret;
227 }
228
229 assert(*out_length == length);
Lionel Debieve02a48072020-07-17 12:35:30 +0200230 cur->pos += *out_length;
Lionel Debieve64a524d2019-09-09 20:13:34 +0200231
232 return 0;
233}
234
235static int mtd_close(io_entity_t *entity)
236{
237 entity->info = (uintptr_t)NULL;
238
239 return 0;
240}
241
242static 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
272static 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 */
280int 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}