blob: b8c1d6479ff8af7045a05cecdabe90ab58fbd8c4 [file] [log] [blame]
James Morrisseyf2f9bb52014-02-10 16:18:59 +00001/*
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +00002 * Copyright (c) 2014-2017, 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 */
Dan Handleye2712bc2014-04-10 15:37:22 +010035static int is_valid_dev_connector(const io_dev_connector_t *dev_con)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000036{
37 int result = (dev_con != NULL) && (dev_con->dev_open != NULL);
38 return result;
39}
40
41
42/* Return a boolean value indicating whether a device handle is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010043static int is_valid_dev(const uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000044{
Dan Handleya4cb68e2014-04-23 13:47:06 +010045 const io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000046 int result = (dev != NULL) && (dev->funcs != NULL) &&
47 (dev->funcs->type != NULL) &&
48 (dev->funcs->type() < IO_TYPE_MAX);
49 return result;
50}
51
52
53/* Return a boolean value indicating whether an IO entity is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010054static int is_valid_entity(const uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000055{
Dan Handleya4cb68e2014-04-23 13:47:06 +010056 const io_entity_t *entity = (io_entity_t *)handle;
57 int result = (entity != NULL) &&
58 (is_valid_dev((uintptr_t)entity->dev_handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +000059 return result;
60}
61
62
63/* Return a boolean value indicating whether a seek mode is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010064static int is_valid_seek_mode(io_seek_mode_t mode)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000065{
66 return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX));
67}
68
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +000069#endif /* ENABLE_ASSERTIONS */
70/* End of extra validation functions only used when asserts are enabled */
James Morrisseyf2f9bb52014-02-10 16:18:59 +000071
72
73/* Open a connection to a specific device */
Dan Handleya4cb68e2014-04-23 13:47:06 +010074static int dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
Dan Handleye2712bc2014-04-10 15:37:22 +010075 io_dev_info_t **dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000076{
Juan Castillo6e762062015-11-02 10:47:01 +000077 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000078 assert(dev_info != NULL);
79 assert(is_valid_dev_connector(dev_con));
80
81 result = dev_con->dev_open(dev_spec, dev_info);
82 return result;
83}
84
85
86/* Set a handle to track an entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +010087static void set_handle(uintptr_t *handle, io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000088{
89 assert(handle != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +010090 *handle = (uintptr_t)entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000091}
92
93
94/* Locate an entity in the pool, specified by address */
Dan Handleya4cb68e2014-04-23 13:47:06 +010095static int find_first_entity(const io_entity_t *entity, unsigned int *index_out)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000096{
Juan Castillo6e762062015-11-02 10:47:01 +000097 int result = -ENOENT;
Etienne Carriere48708f02017-06-07 16:43:19 +020098 for (unsigned int index = 0; index < MAX_IO_HANDLES; ++index) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +000099 if (entity_map[index] == entity) {
Juan Castillo6e762062015-11-02 10:47:01 +0000100 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000101 *index_out = index;
102 break;
103 }
104 }
105 return result;
106}
107
108
109/* Allocate an entity from the pool and return a pointer to it */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100110static int allocate_entity(io_entity_t **entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000111{
Juan Castillo6e762062015-11-02 10:47:01 +0000112 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000113 assert(entity != NULL);
114
115 if (entity_count < MAX_IO_HANDLES) {
116 unsigned int index = 0;
117 result = find_first_entity(NULL, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000118 assert(result == 0);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000119 *entity = entity_map[index] = &entity_pool[index];
120 ++entity_count;
Juan Castillo6e762062015-11-02 10:47:01 +0000121 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000122
123 return result;
124}
125
126
127/* Release an entity back to the pool */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100128static int free_entity(const io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000129{
Juan Castillo6e762062015-11-02 10:47:01 +0000130 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000131 unsigned int index = 0;
132 assert(entity != NULL);
133
134 result = find_first_entity(entity, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000135 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000136 entity_map[index] = NULL;
137 --entity_count;
138 }
139
140 return result;
141}
142
143
144/* Exported API */
145
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000146/* Register a device driver */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100147int io_register_device(const io_dev_info_t *dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000148{
Juan Castillo6e762062015-11-02 10:47:01 +0000149 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000150 assert(dev_info != NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000151
Dan Handley3aa92162014-08-04 18:31:43 +0100152 if (dev_count < MAX_IO_DEVICES) {
153 devices[dev_count] = dev_info;
154 dev_count++;
Juan Castillo6e762062015-11-02 10:47:01 +0000155 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000156 }
157
158 return result;
159}
160
161
162/* Open a connection to an IO device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100163int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
164 uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000165{
Juan Castillo6e762062015-11-02 10:47:01 +0000166 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000167 assert(handle != NULL);
168
Dan Handleya4cb68e2014-04-23 13:47:06 +0100169 result = dev_open(dev_con, dev_spec, (io_dev_info_t **)handle);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000170 return result;
171}
172
173
174/* Initialise an IO device explicitly - to permit lazy initialisation or
175 * re-initialisation */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100176int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000177{
Juan Castillo6e762062015-11-02 10:47:01 +0000178 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100179 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000180 assert(is_valid_dev(dev_handle));
181
Dan Handleya4cb68e2014-04-23 13:47:06 +0100182 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000183
Juan Castillo6e762062015-11-02 10:47:01 +0000184 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000185 if (dev->funcs->dev_init != NULL) {
186 result = dev->funcs->dev_init(dev, init_params);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000187 }
Juan Castillo6e762062015-11-02 10:47:01 +0000188
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000189 return result;
190}
191
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000192/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100193int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000194{
Juan Castillo6e762062015-11-02 10:47:01 +0000195 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100196 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000197 assert(is_valid_dev(dev_handle));
198
Dan Handleya4cb68e2014-04-23 13:47:06 +0100199 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000200
Juan Castillo6e762062015-11-02 10:47:01 +0000201 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000202 if (dev->funcs->dev_close != NULL) {
203 result = dev->funcs->dev_close(dev);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000204 }
205
206 return result;
207}
208
209
210/* Synchronous operations */
211
212
213/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100214int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000215{
Juan Castillo6e762062015-11-02 10:47:01 +0000216 int result;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100217 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000218 assert(is_valid_dev(dev_handle));
219
Dan Handleya4cb68e2014-04-23 13:47:06 +0100220 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100221 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000222
223 result = allocate_entity(&entity);
224
Juan Castillo6e762062015-11-02 10:47:01 +0000225 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000226 assert(dev->funcs->open != NULL);
227 result = dev->funcs->open(dev, spec, entity);
228
Juan Castillo6e762062015-11-02 10:47:01 +0000229 if (result == 0) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100230 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000231 set_handle(handle, entity);
232 } else
233 free_entity(entity);
234 }
235 return result;
236}
237
238
239/* Seek to a specific position in an IO entity */
Yann Gautierf30cddc2019-04-16 11:35:19 +0200240int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000241{
Juan Castillo6e762062015-11-02 10:47:01 +0000242 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000243 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
244
Dan Handleya4cb68e2014-04-23 13:47:06 +0100245 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000246
Dan Handleye2712bc2014-04-10 15:37:22 +0100247 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000248
249 if (dev->funcs->seek != NULL)
250 result = dev->funcs->seek(entity, mode, offset);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000251
252 return result;
253}
254
255
256/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100257int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000258{
Juan Castillo6e762062015-11-02 10:47:01 +0000259 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000260 assert(is_valid_entity(handle) && (length != NULL));
261
Dan Handleya4cb68e2014-04-23 13:47:06 +0100262 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000263
Dan Handleye2712bc2014-04-10 15:37:22 +0100264 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000265
266 if (dev->funcs->size != NULL)
267 result = dev->funcs->size(entity, length);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000268
269 return result;
270}
271
272
273/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100274int io_read(uintptr_t handle,
275 uintptr_t buffer,
276 size_t length,
277 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000278{
Juan Castillo6e762062015-11-02 10:47:01 +0000279 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300280 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000281
Dan Handleya4cb68e2014-04-23 13:47:06 +0100282 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000283
Dan Handleye2712bc2014-04-10 15:37:22 +0100284 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000285
286 if (dev->funcs->read != NULL)
287 result = dev->funcs->read(entity, buffer, length, length_read);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000288
289 return result;
290}
291
292
293/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100294int io_write(uintptr_t handle,
295 const uintptr_t buffer,
296 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000297 size_t *length_written)
298{
Juan Castillo6e762062015-11-02 10:47:01 +0000299 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300300 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000301
Dan Handleya4cb68e2014-04-23 13:47:06 +0100302 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000303
Dan Handleye2712bc2014-04-10 15:37:22 +0100304 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000305
306 if (dev->funcs->write != NULL) {
307 result = dev->funcs->write(entity, buffer, length,
308 length_written);
Juan Castillo6e762062015-11-02 10:47:01 +0000309 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000310
311 return result;
312}
313
314
315/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100316int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000317{
Juan Castillo6e762062015-11-02 10:47:01 +0000318 int result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000319 assert(is_valid_entity(handle));
320
Dan Handleya4cb68e2014-04-23 13:47:06 +0100321 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000322
Dan Handleye2712bc2014-04-10 15:37:22 +0100323 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000324
Juan Castillo6e762062015-11-02 10:47:01 +0000325 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000326 if (dev->funcs->close != NULL)
327 result = dev->funcs->close(entity);
Juan Castillo6e762062015-11-02 10:47:01 +0000328
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000329 /* Ignore improbable free_entity failure */
330 (void)free_entity(entity);
331
332 return result;
333}