xref: /arm-trusted-firmware/drivers/io/io_mtd.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
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