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