]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/bhnd_erom.c
MFV: r329072
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / bhnd_erom.c
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         free(drivers, M_TEMP);
149         return (erom_cls);
150 }
151
152 /**
153  * Allocate and return a new device enumeration table parser.
154  * 
155  * @param cls           The parser class for which an instance will be
156  *                      allocated.
157  * @param eio           The bus I/O callbacks to use when reading the device
158  *                      enumeration table.
159  * @param cid           The device's chip identifier.
160  *
161  * @retval non-NULL     success
162  * @retval NULL         if an error occured allocating or initializing the
163  *                      EROM parser.
164  */
165 bhnd_erom_t *
166 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
167     struct bhnd_erom_io *eio)
168 {
169         bhnd_erom_t     *erom;
170         int              error;
171
172         erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
173             M_WAITOK|M_ZERO);
174
175         if ((error = BHND_EROM_INIT(erom, cid, eio))) {
176                 printf("error initializing %s parser at %#jx: %d\n", cls->name,
177                     (uintmax_t)cid->enum_addr, error);
178
179                 kobj_delete((kobj_t)erom, M_BHND);
180                 return (NULL);
181         }
182
183         return (erom);
184 }
185
186 /**
187  * Perform static initialization of a device enumeration table parser.
188  * 
189  * This may be used to initialize a caller-allocated erom instance state
190  * during early boot, prior to malloc availability.
191  * 
192  * @param cls           The parser class for which an instance will be
193  *                      allocated.
194  * @param erom          The erom parser instance to initialize.
195  * @param esize         The total available number of bytes allocated for
196  *                      @p erom. If this is less than is required by @p cls,
197  *                      ENOMEM will be returned.
198  * @param cid           The device's chip identifier.
199  * @param eio           The bus I/O callbacks to use when reading the device
200  *                      enumeration table.
201  *
202  * @retval 0            success
203  * @retval ENOMEM       if @p esize is smaller than required by @p cls.
204  * @retval non-zero     if an error occurs initializing the EROM parser,
205  *                      a regular unix error code will be returned.
206  */
207 int
208 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
209     const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
210 {
211         kobj_class_t    kcls;
212
213         kcls = (kobj_class_t)cls;
214
215         /* Verify allocation size */
216         if (kcls->size > esize)
217                 return (ENOMEM);
218
219         /* Perform instance initialization */
220         kobj_init_static((kobj_t)erom, kcls);
221         return (BHND_EROM_INIT(erom, cid, eio)); 
222 }
223
224 /**
225  * Release any resources held by a @p erom parser previously
226  * initialized via bhnd_erom_init_static().
227  * 
228  * @param       erom    An erom parser instance previously initialized via
229  *                      bhnd_erom_init_static().
230  */
231 void
232 bhnd_erom_fini_static(bhnd_erom_t *erom)
233 {
234         return (BHND_EROM_FINI(erom));
235 }
236
237 /**
238  * Release all resources held by a @p erom parser previously
239  * allocated via bhnd_erom_alloc().
240  * 
241  * @param       erom    An erom parser instance previously allocated via
242  *                      bhnd_erom_alloc().
243  */
244 void
245 bhnd_erom_free(bhnd_erom_t *erom)
246 {
247         BHND_EROM_FINI(erom);
248         kobj_delete((kobj_t)erom, M_BHND);
249 }
250
251
252 /**
253  * Attempt to map @p size bytes at @p addr, replacing any existing
254  * @p eio mapping.
255  * 
256  * @param eio   I/O instance state.
257  * @param addr  The address to be mapped.
258  * @param size  The number of bytes to be mapped at @p addr.
259  * 
260  * @retval 0            success
261  * @retval non-zero     if mapping @p addr otherwise fails, a regular
262  *                      unix error code should be returned.
263  */
264 int
265 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
266 {
267         return (eio->map(eio, addr, size));
268 }
269
270 /**
271  * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
272  * relative to @p eio's current mapping.
273  * 
274  * @param eio           erom I/O callbacks
275  * @param offset        read offset.
276  * @param width         item width (1, 2, or 4 bytes).
277  */
278 uint32_t
279 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
280 {
281         return (eio->read(eio, offset, width));
282 }
283
284 /**
285  * Free all resources held by @p eio.
286  */
287 void
288 bhnd_erom_io_fini(struct bhnd_erom_io *eio)
289 {
290         if (eio->fini != NULL)
291                 return (eio->fini(eio));
292 }
293
294 /**
295  * Allocate, initialize, and return a new I/O instance that will perform
296  * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
297  * 
298  * @param dev   The device to pass to bhnd_alloc_resource() and
299  *              bhnd_release_resource() functions.
300  * @param rid   The resource ID to be used when allocating memory resources.
301  */
302 struct bhnd_erom_io *
303 bhnd_erom_iores_new(device_t dev, int rid)
304 {
305         struct bhnd_erom_iores  *iores;
306
307         iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
308         iores->eio.map = bhnd_erom_iores_map;
309         iores->eio.read = bhnd_erom_iores_read;
310         iores->eio.fini = bhnd_erom_iores_fini;
311
312         iores->owner = dev;
313         iores->owner_rid = rid;
314         iores->mapped = NULL;
315         iores->mapped_rid = -1;
316
317         return (&iores->eio);
318 }
319
320 static int
321 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
322     bhnd_size_t size)
323 {
324         struct bhnd_erom_iores *iores;
325
326         iores = (struct bhnd_erom_iores *)eio;
327
328         /* Sanity check the addr/size */
329         if (size == 0)
330                 return (EINVAL);
331
332         if (BHND_ADDR_MAX - size < addr)
333                 return (EINVAL);        /* would overflow */
334
335         /* Check for an existing mapping */
336         if (iores->mapped) {
337                 /* If already mapped, nothing else to do */
338                 if (rman_get_start(iores->mapped->res) == addr &&
339                     rman_get_size(iores->mapped->res) == size)
340                 {
341                         return (0);
342                 }
343
344                 /* Otherwise, we need to drop the existing mapping */
345                 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
346                     iores->mapped_rid, iores->mapped);
347                 iores->mapped = NULL;
348                 iores->mapped_rid = -1;
349         }
350
351         /* Try to allocate the new mapping */
352         iores->mapped_rid = iores->owner_rid;
353         iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
354             &iores->mapped_rid, addr, addr+size-1, size,
355             RF_ACTIVE|RF_SHAREABLE);
356         if (iores->mapped == NULL) {
357                 iores->mapped_rid = -1;
358                 return (ENXIO);
359         }
360
361         return (0);
362 }
363
364 static uint32_t
365 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
366 {
367         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
368
369         if (iores->mapped == NULL)
370                 panic("read with invalid mapping");
371
372         switch (width) {
373         case 1:
374                 return (bhnd_bus_read_1(iores->mapped, offset));
375         case 2:
376                 return (bhnd_bus_read_2(iores->mapped, offset));
377         case 4:
378                 return (bhnd_bus_read_4(iores->mapped, offset));
379         default:
380                 panic("invalid width %u", width);
381         }
382 }
383
384 static void
385 bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
386 {
387         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
388
389         /* Release any mapping */
390         if (iores->mapped) {
391                 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
392                     iores->mapped_rid, iores->mapped);
393                 iores->mapped = NULL;
394                 iores->mapped_rid = -1;
395         }
396
397         free(eio, M_BHND);
398 }
399
400 /**
401  * Initialize an I/O instance that will perform mapping directly from the
402  * given bus space tag and handle.
403  * 
404  * @param iobus The I/O instance to be initialized.
405  * @param addr  The base address mapped by @p bsh.
406  * @param size  The total size mapped by @p bsh.
407  * @param bst   Bus space tag for @p bsh.
408  * @param bsh   Bus space handle mapping the full bus enumeration space.
409  * 
410  * @retval 0            success
411  * @retval non-zero     if initializing @p iobus otherwise fails, a regular
412  *                      unix error code will be returned.
413  */
414 int
415 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
416     bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
417 {
418         iobus->eio.map = bhnd_erom_iobus_map;
419         iobus->eio.read = bhnd_erom_iobus_read;
420         iobus->eio.fini = NULL;
421
422         iobus->addr = addr;
423         iobus->size = size;
424         iobus->bst = bst;
425         iobus->bsh = bsh;
426         iobus->mapped = false;
427
428         return (0);
429 }
430
431 static int
432 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
433     bhnd_size_t size)
434 {
435         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
436
437         /* Sanity check the addr/size */
438         if (size == 0)
439                 return (EINVAL);
440
441         /* addr+size must not overflow */
442         if (BHND_ADDR_MAX - size < addr)
443                 return (EINVAL);
444
445         /* addr/size must fit within our bus tag's mapping */
446         if (addr < iobus->addr || size > iobus->size)
447                 return (ENXIO);
448
449         if (iobus->size - (addr - iobus->addr) < size)
450                 return (ENXIO);
451
452         /* The new addr offset and size must be representible as a bus_size_t */
453         if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
454                 return (ENXIO);
455
456         if (size > BUS_SPACE_MAXSIZE)
457                 return (ENXIO);
458
459         iobus->offset = addr - iobus->addr;
460         iobus->limit = size;
461         iobus->mapped = true;
462
463         return (0);
464 }
465
466 static uint32_t
467 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
468 {
469         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
470
471         if (!iobus->mapped) 
472                 panic("no active mapping");
473
474         if (iobus->limit < width || iobus->limit - width < offset)
475                 panic("invalid offset %#jx", offset);
476
477         switch (width) {
478         case 1:
479                 return (bus_space_read_1(iobus->bst, iobus->bsh,
480                     iobus->offset + offset));
481         case 2:
482                 return (bus_space_read_2(iobus->bst, iobus->bsh,
483                     iobus->offset + offset));
484         case 4:
485                 return (bus_space_read_4(iobus->bst, iobus->bsh,
486                     iobus->offset + offset));
487         default:
488                 panic("invalid width %u", width);
489         }
490 }