xref: /freebsd/sys/dev/bhnd/bhnd_erom.c (revision 97cb52fa9aefd90fad38790fded50905aeeb9b9e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
5  * Copyright (c) 2017 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * Portions of this software were developed by Landon Fuller
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
19  *    redistribution must be conditioned upon including a substantially
20  *    similar Disclaimer requirement for further binary redistribution.
21  *
22  * NO WARRANTY
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
26  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
27  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
28  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
31  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGES.
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/kobj.h>
42 
43 #include <machine/bus.h>
44 #include <sys/rman.h>
45 #include <machine/resource.h>
46 
47 #include <dev/bhnd/bhndvar.h>
48 #include <dev/bhnd/bhnd_erom.h>
49 #include <dev/bhnd/bhnd_eromvar.h>
50 
51 static int	bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
52 		    bhnd_size_t size);
53 static uint32_t	bhnd_erom_iores_read(struct bhnd_erom_io *eio,
54 		    bhnd_size_t offset, u_int width);
55 static void	bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
56 
57 static int	bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
58 		    bhnd_size_t size);
59 static uint32_t	bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
60 		    bhnd_size_t offset, u_int width);
61 
62 /**
63  * An implementation of bhnd_erom_io that manages mappings via
64  * bhnd_alloc_resource() and bhnd_release_resource().
65  */
66 struct bhnd_erom_iores {
67 	struct bhnd_erom_io	 eio;
68 	device_t		 owner;		/**< device from which we'll allocate resources */
69 	int			 owner_rid;	/**< rid to use when allocating new mappings */
70 	struct bhnd_resource	*mapped;	/**< current mapping, or NULL */
71 	int			 mapped_rid;	/**< resource ID of current mapping, or -1 */
72 };
73 
74 /**
75  * Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
76  * registered for @p bus_devclass, probe @p eio for supporting parser classes,
77  * and return the best available supporting enumeration parser class.
78  *
79  * @param	bus_devclass	The bus device class to be queried for
80  *				bhnd(4)-compatible drivers.
81  * @param	eio		An erom bus I/O instance, configured with a
82  *				mapping of the first bus core.
83  * @param	hint		Identification hint used to identify the device.
84  *				If the chipset supports standard chip
85  *				identification registers within the first core,
86  *				this parameter should be NULL.
87  * @param[out]	cid		On success, the probed chip identifier.
88  *
89  * @retval non-NULL	on success, the best available EROM class.
90  * @retval NULL		if no erom class returned a successful probe result for
91  *			@p eio.
92  */
93 bhnd_erom_class_t *
94 bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
95     struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
96     struct bhnd_chipid *cid)
97 {
98 	driver_t		**drivers;
99 	int			 drv_count;
100 	bhnd_erom_class_t	*erom_cls;
101 	int			 error, prio, result;
102 
103 	erom_cls = NULL;
104 	prio = 0;
105 
106 	/* Fetch all available drivers */
107 	error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
108 	if (error) {
109 		printf("error fetching bhnd(4) drivers for %s: %d\n",
110 		    devclass_get_name(bus_devclass), error);
111 		return (NULL);
112 	}
113 
114 	/* Enumerate the drivers looking for the best available EROM class */
115 	for (int i = 0; i < drv_count; i++) {
116 		struct bhnd_chipid	 pcid;
117 		bhnd_erom_class_t	*cls;
118 
119 		/* The default implementation of BHND_BUS_GET_EROM_CLASS()
120 		 * returns NULL if unimplemented; this should always be safe
121 		 * to call on arbitrary drivers */
122 		cls = bhnd_driver_get_erom_class(drivers[i]);
123 		if (cls == NULL)
124 			continue;
125 
126 		kobj_class_compile(cls);
127 
128 		/* Probe the bus */
129 		result = bhnd_erom_probe(cls, eio, hint, &pcid);
130 
131 		/* The parser did not match if an error was returned */
132 		if (result > 0)
133 			continue;
134 
135 		/* Check for a new highest priority match */
136 		if (erom_cls == NULL || result > prio) {
137 			prio = result;
138 
139 			*cid = pcid;
140 			erom_cls = cls;
141 		}
142 
143 		/* Terminate immediately on BUS_PROBE_SPECIFIC */
144 		if (result == BUS_PROBE_SPECIFIC)
145 			break;
146 	}
147 
148 	return (erom_cls);
149 }
150 
151 /**
152  * Allocate and return a new device enumeration table parser.
153  *
154  * @param cls		The parser class for which an instance will be
155  *			allocated.
156  * @param eio		The bus I/O callbacks to use when reading the device
157  *			enumeration table.
158  * @param cid		The device's chip identifier.
159  *
160  * @retval non-NULL	success
161  * @retval NULL		if an error occured allocating or initializing the
162  *			EROM parser.
163  */
164 bhnd_erom_t *
165 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
166     struct bhnd_erom_io *eio)
167 {
168 	bhnd_erom_t	*erom;
169 	int		 error;
170 
171 	erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
172 	    M_WAITOK|M_ZERO);
173 
174 	if ((error = BHND_EROM_INIT(erom, cid, eio))) {
175 		printf("error initializing %s parser at %#jx: %d\n", cls->name,
176 		    (uintmax_t)cid->enum_addr, error);
177 
178 		kobj_delete((kobj_t)erom, M_BHND);
179 		return (NULL);
180 	}
181 
182 	return (erom);
183 }
184 
185 /**
186  * Perform static initialization of a device enumeration table parser.
187  *
188  * This may be used to initialize a caller-allocated erom instance state
189  * during early boot, prior to malloc availability.
190  *
191  * @param cls		The parser class for which an instance will be
192  *			allocated.
193  * @param erom		The erom parser instance to initialize.
194  * @param esize		The total available number of bytes allocated for
195  *			@p erom. If this is less than is required by @p cls,
196  *			ENOMEM will be returned.
197  * @param cid		The device's chip identifier.
198  * @param eio		The bus I/O callbacks to use when reading the device
199  *			enumeration table.
200  *
201  * @retval 0		success
202  * @retval ENOMEM	if @p esize is smaller than required by @p cls.
203  * @retval non-zero	if an error occurs initializing the EROM parser,
204  *			a regular unix error code will be returned.
205  */
206 int
207 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
208     const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
209 {
210 	kobj_class_t	kcls;
211 
212 	kcls = (kobj_class_t)cls;
213 
214 	/* Verify allocation size */
215 	if (kcls->size > esize)
216 		return (ENOMEM);
217 
218 	/* Perform instance initialization */
219 	kobj_init_static((kobj_t)erom, kcls);
220 	return (BHND_EROM_INIT(erom, cid, eio));
221 }
222 
223 /**
224  * Release any resources held by a @p erom parser previously
225  * initialized via bhnd_erom_init_static().
226  *
227  * @param	erom	An erom parser instance previously initialized via
228  *			bhnd_erom_init_static().
229  */
230 void
231 bhnd_erom_fini_static(bhnd_erom_t *erom)
232 {
233 	return (BHND_EROM_FINI(erom));
234 }
235 
236 /**
237  * Release all resources held by a @p erom parser previously
238  * allocated via bhnd_erom_alloc().
239  *
240  * @param	erom	An erom parser instance previously allocated via
241  *			bhnd_erom_alloc().
242  */
243 void
244 bhnd_erom_free(bhnd_erom_t *erom)
245 {
246 	BHND_EROM_FINI(erom);
247 	kobj_delete((kobj_t)erom, M_BHND);
248 }
249 
250 
251 /**
252  * Attempt to map @p size bytes at @p addr, replacing any existing
253  * @p eio mapping.
254  *
255  * @param eio	I/O instance state.
256  * @param addr	The address to be mapped.
257  * @param size	The number of bytes to be mapped at @p addr.
258  *
259  * @retval 0		success
260  * @retval non-zero	if mapping @p addr otherwise fails, a regular
261  *			unix error code should be returned.
262  */
263 int
264 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
265 {
266 	return (eio->map(eio, addr, size));
267 }
268 
269 /**
270  * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
271  * relative to @p eio's current mapping.
272  *
273  * @param eio		erom I/O callbacks
274  * @param offset	read offset.
275  * @param width		item width (1, 2, or 4 bytes).
276  */
277 uint32_t
278 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
279 {
280 	return (eio->read(eio, offset, width));
281 }
282 
283 /**
284  * Free all resources held by @p eio.
285  */
286 void
287 bhnd_erom_io_fini(struct bhnd_erom_io *eio)
288 {
289 	if (eio->fini != NULL)
290 		return (eio->fini(eio));
291 }
292 
293 /**
294  * Allocate, initialize, and return a new I/O instance that will perform
295  * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
296  *
297  * @param dev	The device to pass to bhnd_alloc_resource() and
298  *		bhnd_release_resource() functions.
299  * @param rid	The resource ID to be used when allocating memory resources.
300  */
301 struct bhnd_erom_io *
302 bhnd_erom_iores_new(device_t dev, int rid)
303 {
304 	struct bhnd_erom_iores	*iores;
305 
306 	iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
307 	iores->eio.map = bhnd_erom_iores_map;
308 	iores->eio.read = bhnd_erom_iores_read;
309 	iores->eio.fini = bhnd_erom_iores_fini;
310 
311 	iores->owner = dev;
312 	iores->owner_rid = rid;
313 	iores->mapped = NULL;
314 	iores->mapped_rid = -1;
315 
316 	return (&iores->eio);
317 }
318 
319 static int
320 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
321     bhnd_size_t size)
322 {
323 	struct bhnd_erom_iores *iores;
324 
325 	iores = (struct bhnd_erom_iores *)eio;
326 
327 	/* Sanity check the addr/size */
328 	if (size == 0)
329 		return (EINVAL);
330 
331 	if (BHND_ADDR_MAX - size < addr)
332 		return (EINVAL);	/* would overflow */
333 
334 	/* Check for an existing mapping */
335 	if (iores->mapped) {
336 		/* If already mapped, nothing else to do */
337 		if (rman_get_start(iores->mapped->res) == addr &&
338 		    rman_get_size(iores->mapped->res) == size)
339 		{
340 			return (0);
341 		}
342 
343 		/* Otherwise, we need to drop the existing mapping */
344 		bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
345 		    iores->mapped_rid, iores->mapped);
346 		iores->mapped = NULL;
347 		iores->mapped_rid = -1;
348 	}
349 
350 	/* Try to allocate the new mapping */
351 	iores->mapped_rid = iores->owner_rid;
352 	iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
353 	    &iores->mapped_rid, addr, addr+size-1, size,
354 	    RF_ACTIVE|RF_SHAREABLE);
355 	if (iores->mapped == NULL) {
356 		iores->mapped_rid = -1;
357 		return (ENXIO);
358 	}
359 
360 	return (0);
361 }
362 
363 static uint32_t
364 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
365 {
366 	struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
367 
368 	if (iores->mapped == NULL)
369 		panic("read with invalid mapping");
370 
371 	switch (width) {
372 	case 1:
373 		return (bhnd_bus_read_1(iores->mapped, offset));
374 	case 2:
375 		return (bhnd_bus_read_2(iores->mapped, offset));
376 	case 4:
377 		return (bhnd_bus_read_4(iores->mapped, offset));
378 	default:
379 		panic("invalid width %u", width);
380 	}
381 }
382 
383 static void
384 bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
385 {
386 	struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
387 
388 	/* Release any mapping */
389 	if (iores->mapped) {
390 		bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
391 		    iores->mapped_rid, iores->mapped);
392 		iores->mapped = NULL;
393 		iores->mapped_rid = -1;
394 	}
395 
396 	free(eio, M_BHND);
397 }
398 
399 /**
400  * Initialize an I/O instance that will perform mapping directly from the
401  * given bus space tag and handle.
402  *
403  * @param iobus	The I/O instance to be initialized.
404  * @param addr	The base address mapped by @p bsh.
405  * @param size	The total size mapped by @p bsh.
406  * @param bst	Bus space tag for @p bsh.
407  * @param bsh	Bus space handle mapping the full bus enumeration space.
408  *
409  * @retval 0		success
410  * @retval non-zero	if initializing @p iobus otherwise fails, a regular
411  *			unix error code will be returned.
412  */
413 int
414 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
415     bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
416 {
417 	iobus->eio.map = bhnd_erom_iobus_map;
418 	iobus->eio.read = bhnd_erom_iobus_read;
419 	iobus->eio.fini = NULL;
420 
421 	iobus->addr = addr;
422 	iobus->size = size;
423 	iobus->bst = bst;
424 	iobus->bsh = bsh;
425 	iobus->mapped = false;
426 
427 	return (0);
428 }
429 
430 static int
431 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
432     bhnd_size_t size)
433 {
434 	struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
435 
436 	/* Sanity check the addr/size */
437 	if (size == 0)
438 		return (EINVAL);
439 
440 	/* addr+size must not overflow */
441 	if (BHND_ADDR_MAX - size < addr)
442 		return (EINVAL);
443 
444 	/* addr/size must fit within our bus tag's mapping */
445 	if (addr < iobus->addr || size > iobus->size)
446 		return (ENXIO);
447 
448 	if (iobus->size - (addr - iobus->addr) < size)
449 		return (ENXIO);
450 
451 	/* The new addr offset and size must be representible as a bus_size_t */
452 	if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
453 		return (ENXIO);
454 
455 	if (size > BUS_SPACE_MAXSIZE)
456 		return (ENXIO);
457 
458 	iobus->offset = addr - iobus->addr;
459 	iobus->limit = size;
460 	iobus->mapped = true;
461 
462 	return (0);
463 }
464 
465 static uint32_t
466 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
467 {
468 	struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
469 
470 	if (!iobus->mapped)
471 		panic("no active mapping");
472 
473 	if (iobus->limit < width || iobus->limit - width < offset)
474 		panic("invalid offset %#jx", offset);
475 
476 	switch (width) {
477 	case 1:
478 		return (bus_space_read_1(iobus->bst, iobus->bsh,
479 		    iobus->offset + offset));
480 	case 2:
481 		return (bus_space_read_2(iobus->bst, iobus->bsh,
482 		    iobus->offset + offset));
483 	case 4:
484 		return (bus_space_read_4(iobus->bst, iobus->bsh,
485 		    iobus->offset + offset));
486 	default:
487 		panic("invalid width %u", width);
488 	}
489 }
490