1b114abb6SLionel Debieve /* 2*6e86b462SYann Gautier * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved. 3b114abb6SLionel Debieve * 4b114abb6SLionel Debieve * SPDX-License-Identifier: BSD-3-Clause 5b114abb6SLionel Debieve */ 6b114abb6SLionel Debieve 7b114abb6SLionel Debieve #include <assert.h> 8b114abb6SLionel Debieve #include <errno.h> 9b114abb6SLionel Debieve #include <string.h> 10b114abb6SLionel Debieve 11b114abb6SLionel Debieve #include <common/debug.h> 12b114abb6SLionel Debieve #include <drivers/io/io_driver.h> 13b114abb6SLionel Debieve #include <drivers/io/io_mtd.h> 14b114abb6SLionel Debieve #include <lib/utils.h> 15b114abb6SLionel Debieve 16*6e86b462SYann Gautier #include <platform_def.h> 17*6e86b462SYann Gautier 18b114abb6SLionel Debieve typedef struct { 19b114abb6SLionel Debieve io_mtd_dev_spec_t *dev_spec; 20b114abb6SLionel Debieve uintptr_t base; 219a9ea829SLionel Debieve unsigned long long pos; /* Offset in bytes */ 22b114abb6SLionel Debieve unsigned long long size; /* Size of device in bytes */ 239a9ea829SLionel Debieve unsigned long long extra_offset; /* Extra offset in bytes */ 24b114abb6SLionel Debieve } mtd_dev_state_t; 25b114abb6SLionel Debieve 26b114abb6SLionel Debieve io_type_t device_type_mtd(void); 27b114abb6SLionel Debieve 28b114abb6SLionel Debieve static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, 29b114abb6SLionel Debieve io_entity_t *entity); 30b114abb6SLionel Debieve static int mtd_seek(io_entity_t *entity, int mode, signed long long offset); 31b114abb6SLionel Debieve static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, 32b114abb6SLionel Debieve size_t *length_read); 33b114abb6SLionel Debieve static int mtd_close(io_entity_t *entity); 34b114abb6SLionel Debieve static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); 35b114abb6SLionel Debieve static int mtd_dev_close(io_dev_info_t *dev_info); 36b114abb6SLionel Debieve 37b114abb6SLionel Debieve static const io_dev_connector_t mtd_dev_connector = { 38b114abb6SLionel Debieve .dev_open = mtd_dev_open 39b114abb6SLionel Debieve }; 40b114abb6SLionel Debieve 41b114abb6SLionel Debieve static const io_dev_funcs_t mtd_dev_funcs = { 42b114abb6SLionel Debieve .type = device_type_mtd, 43b114abb6SLionel Debieve .open = mtd_open, 44b114abb6SLionel Debieve .seek = mtd_seek, 45b114abb6SLionel Debieve .read = mtd_read, 46b114abb6SLionel Debieve .close = mtd_close, 47b114abb6SLionel Debieve .dev_close = mtd_dev_close, 48b114abb6SLionel Debieve }; 49b114abb6SLionel Debieve 50b114abb6SLionel Debieve static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES]; 51b114abb6SLionel Debieve static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES]; 52b114abb6SLionel Debieve 53b114abb6SLionel Debieve io_type_t device_type_mtd(void) 54b114abb6SLionel Debieve { 55b114abb6SLionel Debieve return IO_TYPE_MTD; 56b114abb6SLionel Debieve } 57b114abb6SLionel Debieve 58b114abb6SLionel Debieve /* Locate a MTD state in the pool, specified by address */ 59b114abb6SLionel Debieve static int find_first_mtd_state(const io_mtd_dev_spec_t *dev_spec, 60b114abb6SLionel Debieve unsigned int *index_out) 61b114abb6SLionel Debieve { 62b114abb6SLionel Debieve unsigned int index; 63b114abb6SLionel Debieve int result = -ENOENT; 64b114abb6SLionel Debieve 65b114abb6SLionel Debieve for (index = 0U; index < MAX_IO_MTD_DEVICES; index++) { 66b114abb6SLionel Debieve /* dev_spec is used as identifier since it's unique */ 67b114abb6SLionel Debieve if (state_pool[index].dev_spec == dev_spec) { 68b114abb6SLionel Debieve result = 0; 69b114abb6SLionel Debieve *index_out = index; 70b114abb6SLionel Debieve break; 71b114abb6SLionel Debieve } 72b114abb6SLionel Debieve } 73b114abb6SLionel Debieve 74b114abb6SLionel Debieve return result; 75b114abb6SLionel Debieve } 76b114abb6SLionel Debieve 77b114abb6SLionel Debieve /* Allocate a device info from the pool */ 78b114abb6SLionel Debieve static int allocate_dev_info(io_dev_info_t **dev_info) 79b114abb6SLionel Debieve { 80b114abb6SLionel Debieve unsigned int index = 0U; 81b114abb6SLionel Debieve int result; 82b114abb6SLionel Debieve 83b114abb6SLionel Debieve result = find_first_mtd_state(NULL, &index); 84b114abb6SLionel Debieve if (result != 0) { 85b114abb6SLionel Debieve return -ENOMEM; 86b114abb6SLionel Debieve } 87b114abb6SLionel Debieve 88b114abb6SLionel Debieve dev_info_pool[index].funcs = &mtd_dev_funcs; 89b114abb6SLionel Debieve dev_info_pool[index].info = (uintptr_t)&state_pool[index]; 90b114abb6SLionel Debieve *dev_info = &dev_info_pool[index]; 91b114abb6SLionel Debieve 92b114abb6SLionel Debieve return 0; 93b114abb6SLionel Debieve } 94b114abb6SLionel Debieve 95b114abb6SLionel Debieve /* Release a device info from the pool */ 96b114abb6SLionel Debieve static int free_dev_info(io_dev_info_t *dev_info) 97b114abb6SLionel Debieve { 98b114abb6SLionel Debieve int result; 99b114abb6SLionel Debieve unsigned int index = 0U; 100b114abb6SLionel Debieve mtd_dev_state_t *state; 101b114abb6SLionel Debieve 102b114abb6SLionel Debieve state = (mtd_dev_state_t *)dev_info->info; 103b114abb6SLionel Debieve result = find_first_mtd_state(state->dev_spec, &index); 104b114abb6SLionel Debieve if (result != 0) { 105b114abb6SLionel Debieve return result; 106b114abb6SLionel Debieve } 107b114abb6SLionel Debieve 108b114abb6SLionel Debieve zeromem(state, sizeof(mtd_dev_state_t)); 109b114abb6SLionel Debieve zeromem(dev_info, sizeof(io_dev_info_t)); 110b114abb6SLionel Debieve 111b114abb6SLionel Debieve return 0; 112b114abb6SLionel Debieve } 113b114abb6SLionel Debieve 1149a9ea829SLionel Debieve static int mtd_add_extra_offset(mtd_dev_state_t *cur, size_t *extra_offset) 1159a9ea829SLionel Debieve { 1169a9ea829SLionel Debieve io_mtd_ops_t *ops = &cur->dev_spec->ops; 1179a9ea829SLionel Debieve int ret; 1189a9ea829SLionel Debieve 1199a9ea829SLionel Debieve if (ops->seek == NULL) { 1209a9ea829SLionel Debieve return 0; 1219a9ea829SLionel Debieve } 1229a9ea829SLionel Debieve 1239a9ea829SLionel Debieve ret = ops->seek(cur->base, cur->pos, extra_offset); 1249a9ea829SLionel Debieve if (ret != 0) { 1259a9ea829SLionel Debieve ERROR("%s: Seek error %d\n", __func__, ret); 1269a9ea829SLionel Debieve return ret; 1279a9ea829SLionel Debieve } 1289a9ea829SLionel Debieve 1299a9ea829SLionel Debieve return 0; 1309a9ea829SLionel Debieve } 1319a9ea829SLionel Debieve 132b114abb6SLionel Debieve static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, 133b114abb6SLionel Debieve io_entity_t *entity) 134b114abb6SLionel Debieve { 135b114abb6SLionel Debieve mtd_dev_state_t *cur; 1369a9ea829SLionel Debieve io_block_spec_t *region; 1379a9ea829SLionel Debieve size_t extra_offset = 0U; 1389a9ea829SLionel Debieve int ret; 139b114abb6SLionel Debieve 140b114abb6SLionel Debieve assert((dev_info->info != 0UL) && (entity->info == 0UL)); 141b114abb6SLionel Debieve 1429a9ea829SLionel Debieve region = (io_block_spec_t *)spec; 143b114abb6SLionel Debieve cur = (mtd_dev_state_t *)dev_info->info; 144b114abb6SLionel Debieve entity->info = (uintptr_t)cur; 1459a9ea829SLionel Debieve cur->base = region->offset; 1469a9ea829SLionel Debieve cur->pos = 0U; 1479a9ea829SLionel Debieve cur->extra_offset = 0U; 1489a9ea829SLionel Debieve 1499a9ea829SLionel Debieve ret = mtd_add_extra_offset(cur, &extra_offset); 1509a9ea829SLionel Debieve if (ret != 0) { 1519a9ea829SLionel Debieve return ret; 1529a9ea829SLionel Debieve } 1539a9ea829SLionel Debieve 1549a9ea829SLionel Debieve cur->base += extra_offset; 155b114abb6SLionel Debieve 156b114abb6SLionel Debieve return 0; 157b114abb6SLionel Debieve } 158b114abb6SLionel Debieve 159b114abb6SLionel Debieve /* Seek to a specific position using offset */ 160b114abb6SLionel Debieve static int mtd_seek(io_entity_t *entity, int mode, signed long long offset) 161b114abb6SLionel Debieve { 162b114abb6SLionel Debieve mtd_dev_state_t *cur; 1639a9ea829SLionel Debieve size_t extra_offset = 0U; 1649a9ea829SLionel Debieve int ret; 165b114abb6SLionel Debieve 166b114abb6SLionel Debieve assert((entity->info != (uintptr_t)NULL) && (offset >= 0)); 167b114abb6SLionel Debieve 168b114abb6SLionel Debieve cur = (mtd_dev_state_t *)entity->info; 169b114abb6SLionel Debieve 170b114abb6SLionel Debieve switch (mode) { 171b114abb6SLionel Debieve case IO_SEEK_SET: 172b114abb6SLionel Debieve if ((offset >= 0) && 173b114abb6SLionel Debieve ((unsigned long long)offset >= cur->size)) { 174b114abb6SLionel Debieve return -EINVAL; 175b114abb6SLionel Debieve } 176b114abb6SLionel Debieve 1779a9ea829SLionel Debieve cur->pos = offset; 178b114abb6SLionel Debieve break; 179b114abb6SLionel Debieve case IO_SEEK_CUR: 1809a9ea829SLionel Debieve if (((cur->base + cur->pos + (unsigned long long)offset) >= 181b114abb6SLionel Debieve cur->size) || 1829a9ea829SLionel Debieve ((cur->base + cur->pos + (unsigned long long)offset) < 1839a9ea829SLionel Debieve cur->base + cur->pos)) { 184b114abb6SLionel Debieve return -EINVAL; 185b114abb6SLionel Debieve } 186b114abb6SLionel Debieve 1879a9ea829SLionel Debieve cur->pos += (unsigned long long)offset; 188b114abb6SLionel Debieve break; 189b114abb6SLionel Debieve default: 190b114abb6SLionel Debieve return -EINVAL; 191b114abb6SLionel Debieve } 192b114abb6SLionel Debieve 1939a9ea829SLionel Debieve ret = mtd_add_extra_offset(cur, &extra_offset); 1949a9ea829SLionel Debieve if (ret != 0) { 1959a9ea829SLionel Debieve return ret; 1969a9ea829SLionel Debieve } 1979a9ea829SLionel Debieve 1989a9ea829SLionel Debieve cur->extra_offset = extra_offset; 1999a9ea829SLionel Debieve 200b114abb6SLionel Debieve return 0; 201b114abb6SLionel Debieve } 202b114abb6SLionel Debieve 203b114abb6SLionel Debieve static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, 204b114abb6SLionel Debieve size_t *out_length) 205b114abb6SLionel Debieve { 206b114abb6SLionel Debieve mtd_dev_state_t *cur; 207b114abb6SLionel Debieve io_mtd_ops_t *ops; 208b114abb6SLionel Debieve int ret; 209b114abb6SLionel Debieve 210b114abb6SLionel Debieve assert(entity->info != (uintptr_t)NULL); 211b114abb6SLionel Debieve assert((length > 0U) && (buffer != (uintptr_t)NULL)); 212b114abb6SLionel Debieve 213b114abb6SLionel Debieve cur = (mtd_dev_state_t *)entity->info; 214b114abb6SLionel Debieve ops = &cur->dev_spec->ops; 215b114abb6SLionel Debieve assert(ops->read != NULL); 216b114abb6SLionel Debieve 217*6e86b462SYann Gautier VERBOSE("Read at %llx into %lx, length %zu\n", 2189a9ea829SLionel Debieve cur->base + cur->pos, buffer, length); 2199a9ea829SLionel Debieve if ((cur->base + cur->pos + length) > cur->dev_spec->device_size) { 220b114abb6SLionel Debieve return -EINVAL; 221b114abb6SLionel Debieve } 222b114abb6SLionel Debieve 2239a9ea829SLionel Debieve ret = ops->read(cur->base + cur->pos + cur->extra_offset, buffer, 2249a9ea829SLionel Debieve length, out_length); 225b114abb6SLionel Debieve if (ret < 0) { 226b114abb6SLionel Debieve return ret; 227b114abb6SLionel Debieve } 228b114abb6SLionel Debieve 229b114abb6SLionel Debieve assert(*out_length == length); 2309a9ea829SLionel Debieve cur->pos += *out_length; 231b114abb6SLionel Debieve 232b114abb6SLionel Debieve return 0; 233b114abb6SLionel Debieve } 234b114abb6SLionel Debieve 235b114abb6SLionel Debieve static int mtd_close(io_entity_t *entity) 236b114abb6SLionel Debieve { 237b114abb6SLionel Debieve entity->info = (uintptr_t)NULL; 238b114abb6SLionel Debieve 239b114abb6SLionel Debieve return 0; 240b114abb6SLionel Debieve } 241b114abb6SLionel Debieve 242b114abb6SLionel Debieve static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) 243b114abb6SLionel Debieve { 244b114abb6SLionel Debieve mtd_dev_state_t *cur; 245b114abb6SLionel Debieve io_dev_info_t *info; 246b114abb6SLionel Debieve io_mtd_ops_t *ops; 247b114abb6SLionel Debieve int result; 248b114abb6SLionel Debieve 249b114abb6SLionel Debieve result = allocate_dev_info(&info); 250b114abb6SLionel Debieve if (result != 0) { 251b114abb6SLionel Debieve return -ENOENT; 252b114abb6SLionel Debieve } 253b114abb6SLionel Debieve 254b114abb6SLionel Debieve cur = (mtd_dev_state_t *)info->info; 255b114abb6SLionel Debieve cur->dev_spec = (io_mtd_dev_spec_t *)dev_spec; 256b114abb6SLionel Debieve *dev_info = info; 257b114abb6SLionel Debieve ops = &(cur->dev_spec->ops); 258b114abb6SLionel Debieve if (ops->init != NULL) { 259b114abb6SLionel Debieve result = ops->init(&cur->dev_spec->device_size, 260b114abb6SLionel Debieve &cur->dev_spec->erase_size); 261b114abb6SLionel Debieve } 262b114abb6SLionel Debieve 263b114abb6SLionel Debieve if (result == 0) { 264b114abb6SLionel Debieve cur->size = cur->dev_spec->device_size; 265b114abb6SLionel Debieve } else { 266b114abb6SLionel Debieve cur->size = 0ULL; 267b114abb6SLionel Debieve } 268b114abb6SLionel Debieve 269b114abb6SLionel Debieve return result; 270b114abb6SLionel Debieve } 271b114abb6SLionel Debieve 272b114abb6SLionel Debieve static int mtd_dev_close(io_dev_info_t *dev_info) 273b114abb6SLionel Debieve { 274b114abb6SLionel Debieve return free_dev_info(dev_info); 275b114abb6SLionel Debieve } 276b114abb6SLionel Debieve 277b114abb6SLionel Debieve /* Exported functions */ 278b114abb6SLionel Debieve 279b114abb6SLionel Debieve /* Register the MTD driver in the IO abstraction */ 280b114abb6SLionel Debieve int register_io_dev_mtd(const io_dev_connector_t **dev_con) 281b114abb6SLionel Debieve { 282b114abb6SLionel Debieve int result; 283b114abb6SLionel Debieve 284b114abb6SLionel Debieve result = io_register_device(&dev_info_pool[0]); 285b114abb6SLionel Debieve if (result == 0) { 286b114abb6SLionel Debieve *dev_con = &mtd_dev_connector; 287b114abb6SLionel Debieve } 288b114abb6SLionel Debieve 289b114abb6SLionel Debieve return result; 290b114abb6SLionel Debieve } 291