blob: c9ff31bcc44b9afc6b1ef0efe7e2558628b2ec39 [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
192
193/* TODO: Consider whether an explicit "shutdown" API should be included */
194
195/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100196int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000197{
Juan Castillo6e762062015-11-02 10:47:01 +0000198 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100199 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000200 assert(is_valid_dev(dev_handle));
201
Dan Handleya4cb68e2014-04-23 13:47:06 +0100202 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000203
Juan Castillo6e762062015-11-02 10:47:01 +0000204 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000205 if (dev->funcs->dev_close != NULL) {
206 result = dev->funcs->dev_close(dev);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000207 }
208
209 return result;
210}
211
212
213/* Synchronous operations */
214
215
216/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100217int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000218{
Juan Castillo6e762062015-11-02 10:47:01 +0000219 int result;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100220 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000221 assert(is_valid_dev(dev_handle));
222
Dan Handleya4cb68e2014-04-23 13:47:06 +0100223 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100224 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000225
226 result = allocate_entity(&entity);
227
Juan Castillo6e762062015-11-02 10:47:01 +0000228 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000229 assert(dev->funcs->open != NULL);
230 result = dev->funcs->open(dev, spec, entity);
231
Juan Castillo6e762062015-11-02 10:47:01 +0000232 if (result == 0) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100233 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000234 set_handle(handle, entity);
235 } else
236 free_entity(entity);
237 }
238 return result;
239}
240
241
242/* Seek to a specific position in an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100243int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000244{
Juan Castillo6e762062015-11-02 10:47:01 +0000245 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000246 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
247
Dan Handleya4cb68e2014-04-23 13:47:06 +0100248 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000249
Dan Handleye2712bc2014-04-10 15:37:22 +0100250 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000251
252 if (dev->funcs->seek != NULL)
253 result = dev->funcs->seek(entity, mode, offset);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000254
255 return result;
256}
257
258
259/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100260int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000261{
Juan Castillo6e762062015-11-02 10:47:01 +0000262 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000263 assert(is_valid_entity(handle) && (length != NULL));
264
Dan Handleya4cb68e2014-04-23 13:47:06 +0100265 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000266
Dan Handleye2712bc2014-04-10 15:37:22 +0100267 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000268
269 if (dev->funcs->size != NULL)
270 result = dev->funcs->size(entity, length);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000271
272 return result;
273}
274
275
276/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100277int io_read(uintptr_t handle,
278 uintptr_t buffer,
279 size_t length,
280 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000281{
Juan Castillo6e762062015-11-02 10:47:01 +0000282 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300283 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000284
Dan Handleya4cb68e2014-04-23 13:47:06 +0100285 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000286
Dan Handleye2712bc2014-04-10 15:37:22 +0100287 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000288
289 if (dev->funcs->read != NULL)
290 result = dev->funcs->read(entity, buffer, length, length_read);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000291
292 return result;
293}
294
295
296/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100297int io_write(uintptr_t handle,
298 const uintptr_t buffer,
299 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000300 size_t *length_written)
301{
Juan Castillo6e762062015-11-02 10:47:01 +0000302 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300303 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000304
Dan Handleya4cb68e2014-04-23 13:47:06 +0100305 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000306
Dan Handleye2712bc2014-04-10 15:37:22 +0100307 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000308
309 if (dev->funcs->write != NULL) {
310 result = dev->funcs->write(entity, buffer, length,
311 length_written);
Juan Castillo6e762062015-11-02 10:47:01 +0000312 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000313
314 return result;
315}
316
317
318/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100319int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000320{
Juan Castillo6e762062015-11-02 10:47:01 +0000321 int result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000322 assert(is_valid_entity(handle));
323
Dan Handleya4cb68e2014-04-23 13:47:06 +0100324 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000325
Dan Handleye2712bc2014-04-10 15:37:22 +0100326 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000327
Juan Castillo6e762062015-11-02 10:47:01 +0000328 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000329 if (dev->funcs->close != NULL)
330 result = dev->funcs->close(entity);
Juan Castillo6e762062015-11-02 10:47:01 +0000331
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000332 /* Ignore improbable free_entity failure */
333 (void)free_entity(entity);
334
335 return result;
336}