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