blob: 053426891da3a06b7113445632029ab3c13fccb9 [file] [log] [blame]
James Morrisseyf2f9bb52014-02-10 16:18:59 +00001/*
johpow0121031282020-07-01 17:09:57 -05002 * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved.
James Morrisseyf2f9bb52014-02-10 16:18:59 +00003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
James Morrisseyf2f9bb52014-02-10 16:18:59 +00005 */
6
James Morrisseyf2f9bb52014-02-10 16:18:59 +00007#include <assert.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +01008#include <stddef.h>
James Morrisseyf2f9bb52014-02-10 16:18:59 +00009
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000010#include <platform_def.h>
11
12#include <drivers/io/io_driver.h>
13#include <drivers/io/io_storage.h>
James Morrisseyf2f9bb52014-02-10 16:18:59 +000014
James Morrisseyf2f9bb52014-02-10 16:18:59 +000015/* Storage for a fixed maximum number of IO entities, definable by platform */
Dan Handleye2712bc2014-04-10 15:37:22 +010016static io_entity_t entity_pool[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000017
18/* Simple way of tracking used storage - each entry is NULL or a pointer to an
19 * entity */
Dan Handleye2712bc2014-04-10 15:37:22 +010020static io_entity_t *entity_map[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000021
22/* Track number of allocated entities */
23static unsigned int entity_count;
24
Dan Handley3aa92162014-08-04 18:31:43 +010025/* Array of fixed maximum of registered devices, definable by platform */
26static const io_dev_info_t *devices[MAX_IO_DEVICES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000027
Dan Handley3aa92162014-08-04 18:31:43 +010028/* Number of currently registered devices */
29static unsigned int dev_count;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000030
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +000031/* Extra validation functions only used when asserts are enabled */
32#if ENABLE_ASSERTIONS
James Morrisseyf2f9bb52014-02-10 16:18:59 +000033
34/* Return a boolean value indicating whether a device connector is valid */
johpow0121031282020-07-01 17:09:57 -050035static bool is_valid_dev_connector(const io_dev_connector_t *dev_con)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000036{
Masahiro Yamada57e86502020-07-09 22:26:37 +090037 return (dev_con != NULL) && (dev_con->dev_open != NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +000038}
39
James Morrisseyf2f9bb52014-02-10 16:18:59 +000040/* Return a boolean value indicating whether a device handle is valid */
johpow0121031282020-07-01 17:09:57 -050041static bool is_valid_dev(const uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000042{
Dan Handleya4cb68e2014-04-23 13:47:06 +010043 const io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Masahiro Yamada57e86502020-07-09 22:26:37 +090044
45 return (dev != NULL) && (dev->funcs != NULL) &&
James Morrisseyf2f9bb52014-02-10 16:18:59 +000046 (dev->funcs->type != NULL) &&
47 (dev->funcs->type() < IO_TYPE_MAX);
James Morrisseyf2f9bb52014-02-10 16:18:59 +000048}
49
50
51/* Return a boolean value indicating whether an IO entity is valid */
johpow0121031282020-07-01 17:09:57 -050052static bool is_valid_entity(const uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000053{
Dan Handleya4cb68e2014-04-23 13:47:06 +010054 const io_entity_t *entity = (io_entity_t *)handle;
Masahiro Yamada57e86502020-07-09 22:26:37 +090055
56 return (entity != NULL) &&
Dan Handleya4cb68e2014-04-23 13:47:06 +010057 (is_valid_dev((uintptr_t)entity->dev_handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +000058}
59
60
61/* Return a boolean value indicating whether a seek mode is valid */
johpow0121031282020-07-01 17:09:57 -050062static bool is_valid_seek_mode(io_seek_mode_t mode)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000063{
64 return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX));
65}
66
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +000067#endif /* ENABLE_ASSERTIONS */
68/* End of extra validation functions only used when asserts are enabled */
James Morrisseyf2f9bb52014-02-10 16:18:59 +000069
70
71/* Open a connection to a specific device */
johpow0121031282020-07-01 17:09:57 -050072static int io_storage_dev_open(const io_dev_connector_t *dev_con,
73 const uintptr_t dev_spec,
Dan Handleye2712bc2014-04-10 15:37:22 +010074 io_dev_info_t **dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000075{
James Morrisseyf2f9bb52014-02-10 16:18:59 +000076 assert(dev_info != NULL);
77 assert(is_valid_dev_connector(dev_con));
78
Masahiro Yamada57e86502020-07-09 22:26:37 +090079 return dev_con->dev_open(dev_spec, dev_info);
James Morrisseyf2f9bb52014-02-10 16:18:59 +000080}
81
82
83/* Set a handle to track an entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +010084static void set_handle(uintptr_t *handle, io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000085{
86 assert(handle != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +010087 *handle = (uintptr_t)entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000088}
89
90
91/* Locate an entity in the pool, specified by address */
Dan Handleya4cb68e2014-04-23 13:47:06 +010092static int find_first_entity(const io_entity_t *entity, unsigned int *index_out)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000093{
Juan Castillo6e762062015-11-02 10:47:01 +000094 int result = -ENOENT;
Etienne Carriere48708f02017-06-07 16:43:19 +020095 for (unsigned int index = 0; index < MAX_IO_HANDLES; ++index) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +000096 if (entity_map[index] == entity) {
Juan Castillo6e762062015-11-02 10:47:01 +000097 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000098 *index_out = index;
99 break;
100 }
101 }
102 return result;
103}
104
105
106/* Allocate an entity from the pool and return a pointer to it */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100107static int allocate_entity(io_entity_t **entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000108{
Juan Castillo6e762062015-11-02 10:47:01 +0000109 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000110 assert(entity != NULL);
111
112 if (entity_count < MAX_IO_HANDLES) {
113 unsigned int index = 0;
114 result = find_first_entity(NULL, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000115 assert(result == 0);
johpow0121031282020-07-01 17:09:57 -0500116 *entity = &entity_pool[index];
117 entity_map[index] = &entity_pool[index];
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000118 ++entity_count;
Juan Castillo6e762062015-11-02 10:47:01 +0000119 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000120
121 return result;
122}
123
124
125/* Release an entity back to the pool */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100126static int free_entity(const io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000127{
Juan Castillo6e762062015-11-02 10:47:01 +0000128 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000129 unsigned int index = 0;
130 assert(entity != NULL);
131
132 result = find_first_entity(entity, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000133 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000134 entity_map[index] = NULL;
135 --entity_count;
136 }
137
138 return result;
139}
140
141
142/* Exported API */
143
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000144/* Register a device driver */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100145int io_register_device(const io_dev_info_t *dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000146{
Juan Castillo6e762062015-11-02 10:47:01 +0000147 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000148 assert(dev_info != NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000149
Dan Handley3aa92162014-08-04 18:31:43 +0100150 if (dev_count < MAX_IO_DEVICES) {
151 devices[dev_count] = dev_info;
152 dev_count++;
Juan Castillo6e762062015-11-02 10:47:01 +0000153 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000154 }
155
156 return result;
157}
158
159
160/* Open a connection to an IO device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100161int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
162 uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000163{
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000164 assert(handle != NULL);
johpow0121031282020-07-01 17:09:57 -0500165 return io_storage_dev_open(dev_con, dev_spec, (io_dev_info_t **)handle);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000166}
167
168
169/* Initialise an IO device explicitly - to permit lazy initialisation or
170 * re-initialisation */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100171int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000172{
Juan Castillo6e762062015-11-02 10:47:01 +0000173 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100174 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000175 assert(is_valid_dev(dev_handle));
176
Dan Handleya4cb68e2014-04-23 13:47:06 +0100177 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000178
Juan Castillo6e762062015-11-02 10:47:01 +0000179 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000180 if (dev->funcs->dev_init != NULL) {
181 result = dev->funcs->dev_init(dev, init_params);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000182 }
Juan Castillo6e762062015-11-02 10:47:01 +0000183
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000184 return result;
185}
186
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000187/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100188int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000189{
Juan Castillo6e762062015-11-02 10:47:01 +0000190 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100191 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000192 assert(is_valid_dev(dev_handle));
193
Dan Handleya4cb68e2014-04-23 13:47:06 +0100194 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000195
Juan Castillo6e762062015-11-02 10:47:01 +0000196 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000197 if (dev->funcs->dev_close != NULL) {
198 result = dev->funcs->dev_close(dev);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000199 }
200
201 return result;
202}
203
204
205/* Synchronous operations */
206
207
208/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100209int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000210{
Juan Castillo6e762062015-11-02 10:47:01 +0000211 int result;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100212 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000213 assert(is_valid_dev(dev_handle));
214
Dan Handleya4cb68e2014-04-23 13:47:06 +0100215 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100216 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000217
218 result = allocate_entity(&entity);
219
Juan Castillo6e762062015-11-02 10:47:01 +0000220 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000221 assert(dev->funcs->open != NULL);
222 result = dev->funcs->open(dev, spec, entity);
223
Juan Castillo6e762062015-11-02 10:47:01 +0000224 if (result == 0) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100225 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000226 set_handle(handle, entity);
227 } else
228 free_entity(entity);
229 }
230 return result;
231}
232
233
234/* Seek to a specific position in an IO entity */
Yann Gautierf30cddc2019-04-16 11:35:19 +0200235int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000236{
Juan Castillo6e762062015-11-02 10:47:01 +0000237 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000238 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
239
Dan Handleya4cb68e2014-04-23 13:47:06 +0100240 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000241
Dan Handleye2712bc2014-04-10 15:37:22 +0100242 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000243
244 if (dev->funcs->seek != NULL)
245 result = dev->funcs->seek(entity, mode, offset);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000246
247 return result;
248}
249
250
251/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100252int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000253{
Juan Castillo6e762062015-11-02 10:47:01 +0000254 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000255 assert(is_valid_entity(handle) && (length != NULL));
256
Dan Handleya4cb68e2014-04-23 13:47:06 +0100257 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000258
Dan Handleye2712bc2014-04-10 15:37:22 +0100259 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000260
261 if (dev->funcs->size != NULL)
262 result = dev->funcs->size(entity, length);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000263
264 return result;
265}
266
267
268/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100269int io_read(uintptr_t handle,
270 uintptr_t buffer,
271 size_t length,
272 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000273{
Juan Castillo6e762062015-11-02 10:47:01 +0000274 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300275 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000276
Dan Handleya4cb68e2014-04-23 13:47:06 +0100277 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000278
Dan Handleye2712bc2014-04-10 15:37:22 +0100279 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000280
281 if (dev->funcs->read != NULL)
282 result = dev->funcs->read(entity, buffer, length, length_read);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000283
284 return result;
285}
286
287
288/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100289int io_write(uintptr_t handle,
290 const uintptr_t buffer,
291 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000292 size_t *length_written)
293{
Juan Castillo6e762062015-11-02 10:47:01 +0000294 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300295 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000296
Dan Handleya4cb68e2014-04-23 13:47:06 +0100297 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000298
Dan Handleye2712bc2014-04-10 15:37:22 +0100299 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000300
301 if (dev->funcs->write != NULL) {
302 result = dev->funcs->write(entity, buffer, length,
303 length_written);
Juan Castillo6e762062015-11-02 10:47:01 +0000304 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000305
306 return result;
307}
308
309
310/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100311int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000312{
Juan Castillo6e762062015-11-02 10:47:01 +0000313 int result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000314 assert(is_valid_entity(handle));
315
Dan Handleya4cb68e2014-04-23 13:47:06 +0100316 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000317
Dan Handleye2712bc2014-04-10 15:37:22 +0100318 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000319
Juan Castillo6e762062015-11-02 10:47:01 +0000320 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000321 if (dev->funcs->close != NULL)
322 result = dev->funcs->close(entity);
Juan Castillo6e762062015-11-02 10:47:01 +0000323
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000324 /* Ignore improbable free_entity failure */
325 (void)free_entity(entity);
326
327 return result;
328}