]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/bhnd_erom.c
riscv: increase GENERICSD gap
[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/bhndreg.h>
48 #include <dev/bhnd/bhndvar.h>
49
50 #include <dev/bhnd/bhnd_erom.h>
51 #include <dev/bhnd/bhnd_eromvar.h>
52
53 #include <dev/bhnd/cores/chipc/chipcreg.h>
54
55 static int      bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
56                     bhnd_size_t size);
57 static int      bhnd_erom_iores_tell(struct bhnd_erom_io *eio,
58                     bhnd_addr_t *addr, bhnd_size_t *size);
59 static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio,
60                     bhnd_size_t offset, u_int width);
61 static void     bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
62
63 static int      bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
64                     bhnd_size_t size);
65 static int      bhnd_erom_iobus_tell(struct bhnd_erom_io *eio,
66                     bhnd_addr_t *addr, bhnd_size_t *size);
67 static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
68                     bhnd_size_t offset, u_int width);
69
70 /**
71  * An implementation of bhnd_erom_io that manages mappings via
72  * bhnd_alloc_resource() and bhnd_release_resource().
73  */
74 struct bhnd_erom_iores {
75         struct bhnd_erom_io      eio;
76         device_t                 owner;         /**< device from which we'll allocate resources */
77         int                      owner_rid;     /**< rid to use when allocating new mappings */
78         struct bhnd_resource    *mapped;        /**< current mapping, or NULL */
79         int                      mapped_rid;    /**< resource ID of current mapping, or -1 */
80 };
81
82 /**
83  * Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
84  * registered for @p bus_devclass, probe @p eio for supporting parser classes,
85  * and return the best available supporting enumeration parser class.
86  * 
87  * @param       bus_devclass    The bus device class to be queried for
88  *                              bhnd(4)-compatible drivers.
89  * @param       eio             An erom bus I/O instance, configured with a
90  *                              mapping of the first bus core.
91  * @param       hint            Identification hint used to identify the device.
92  *                              If the chipset supports standard chip
93  *                              identification registers within the first core,
94  *                              this parameter should be NULL.
95  * @param[out]  cid             On success, the probed chip identifier.
96  * 
97  * @retval non-NULL     on success, the best available EROM class.
98  * @retval NULL         if no erom class returned a successful probe result for
99  *                      @p eio.
100  */
101 bhnd_erom_class_t *
102 bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
103     struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
104     struct bhnd_chipid *cid)
105 {
106         driver_t                **drivers;
107         int                      drv_count;
108         bhnd_erom_class_t       *erom_cls;
109         int                      error, prio, result;
110
111         erom_cls = NULL;
112         prio = 0;
113
114         /* Fetch all available drivers */
115         error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
116         if (error) {
117                 printf("error fetching bhnd(4) drivers for %s: %d\n",
118                     devclass_get_name(bus_devclass), error);
119                 return (NULL);
120         }
121
122         /* Enumerate the drivers looking for the best available EROM class */
123         for (int i = 0; i < drv_count; i++) {
124                 struct bhnd_chipid       pcid;
125                 bhnd_erom_class_t       *cls;
126
127                 /* The default implementation of BHND_BUS_GET_EROM_CLASS()
128                  * returns NULL if unimplemented; this should always be safe
129                  * to call on arbitrary drivers */
130                 cls = bhnd_driver_get_erom_class(drivers[i]);
131                 if (cls == NULL)
132                         continue;
133
134                 kobj_class_compile(cls);
135
136                 /* Probe the bus */
137                 result = bhnd_erom_probe(cls, eio, hint, &pcid);
138
139                 /* The parser did not match if an error was returned */
140                 if (result > 0)
141                         continue;
142                 
143                 /* Check for a new highest priority match */
144                 if (erom_cls == NULL || result > prio) {
145                         prio = result;
146
147                         *cid = pcid;
148                         erom_cls = cls;
149                 }
150
151                 /* Terminate immediately on BUS_PROBE_SPECIFIC */
152                 if (result == BUS_PROBE_SPECIFIC)
153                         break;
154         }
155
156         free(drivers, M_TEMP);
157         return (erom_cls);
158 }
159
160 /**
161  * Allocate and return a new device enumeration table parser.
162  * 
163  * @param cls           The parser class for which an instance will be
164  *                      allocated.
165  * @param eio           The bus I/O callbacks to use when reading the device
166  *                      enumeration table.
167  * @param cid           The device's chip identifier.
168  *
169  * @retval non-NULL     success
170  * @retval NULL         if an error occured allocating or initializing the
171  *                      EROM parser.
172  */
173 bhnd_erom_t *
174 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
175     struct bhnd_erom_io *eio)
176 {
177         bhnd_erom_t     *erom;
178         int              error;
179
180         erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
181             M_WAITOK|M_ZERO);
182
183         if ((error = BHND_EROM_INIT(erom, cid, eio))) {
184                 printf("error initializing %s parser at %#jx: %d\n", cls->name,
185                     (uintmax_t)cid->enum_addr, error);
186
187                 kobj_delete((kobj_t)erom, M_BHND);
188                 return (NULL);
189         }
190
191         return (erom);
192 }
193
194 /**
195  * Perform static initialization of a device enumeration table parser.
196  * 
197  * This may be used to initialize a caller-allocated erom instance state
198  * during early boot, prior to malloc availability.
199  * 
200  * @param cls           The parser class for which an instance will be
201  *                      allocated.
202  * @param erom          The erom parser instance to initialize.
203  * @param esize         The total available number of bytes allocated for
204  *                      @p erom. If this is less than is required by @p cls,
205  *                      ENOMEM will be returned.
206  * @param cid           The device's chip identifier.
207  * @param eio           The bus I/O callbacks to use when reading the device
208  *                      enumeration table.
209  *
210  * @retval 0            success
211  * @retval ENOMEM       if @p esize is smaller than required by @p cls.
212  * @retval non-zero     if an error occurs initializing the EROM parser,
213  *                      a regular unix error code will be returned.
214  */
215 int
216 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
217     const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
218 {
219         kobj_class_t    kcls;
220
221         kcls = (kobj_class_t)cls;
222
223         /* Verify allocation size */
224         if (kcls->size > esize)
225                 return (ENOMEM);
226
227         /* Perform instance initialization */
228         kobj_init_static((kobj_t)erom, kcls);
229         return (BHND_EROM_INIT(erom, cid, eio)); 
230 }
231
232 /**
233  * Release any resources held by a @p erom parser previously
234  * initialized via bhnd_erom_init_static().
235  * 
236  * @param       erom    An erom parser instance previously initialized via
237  *                      bhnd_erom_init_static().
238  */
239 void
240 bhnd_erom_fini_static(bhnd_erom_t *erom)
241 {
242         return (BHND_EROM_FINI(erom));
243 }
244
245 /**
246  * Release all resources held by a @p erom parser previously
247  * allocated via bhnd_erom_alloc().
248  * 
249  * @param       erom    An erom parser instance previously allocated via
250  *                      bhnd_erom_alloc().
251  */
252 void
253 bhnd_erom_free(bhnd_erom_t *erom)
254 {
255         BHND_EROM_FINI(erom);
256         kobj_delete((kobj_t)erom, M_BHND);
257 }
258
259 /**
260  * Read the chip identification registers mapped by @p eio, popuating @p cid
261  * with the parsed result
262  * 
263  * @param       eio             A bus I/O instance, configured with a mapping
264  *                              of the ChipCommon core.
265  * @param[out]  cid             On success, the parsed chip identification.
266  *
267  * @warning
268  * On early siba(4) devices, the ChipCommon core does not provide
269  * a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions
270  * (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return
271  * an invalid `ncores` value.
272  */
273 int
274 bhnd_erom_read_chipid(struct bhnd_erom_io *eio, struct bhnd_chipid *cid)
275 {
276         bhnd_addr_t     cc_addr;
277         bhnd_size_t     cc_size;
278         uint32_t        idreg, cc_caps;
279         int             error;
280
281         /* Fetch ChipCommon address */
282         if ((error = bhnd_erom_io_tell(eio, &cc_addr, &cc_size)))
283                 return (error);
284
285         /* Read chip identifier */
286         idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);
287
288         /* Extract the basic chip info */
289         cid->chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP);
290         cid->chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG);
291         cid->chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV);
292         cid->chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);
293         cid->ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE);
294
295         /* Populate EROM address */
296         if (BHND_CHIPTYPE_HAS_EROM(cid->chip_type)) {
297                 cid->enum_addr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);
298         } else {
299                 cid->enum_addr = cc_addr;
300         }
301
302         /* Populate capability flags */
303         cc_caps = bhnd_erom_io_read(eio, CHIPC_CAPABILITIES, 4);
304         cid->chip_caps = 0x0;
305
306         if (cc_caps & CHIPC_CAP_BKPLN64)
307                 cid->chip_caps |= BHND_CAP_BP64;
308
309         if (cc_caps & CHIPC_CAP_PMU)
310                 cid->chip_caps |= BHND_CAP_PMU;
311
312         return (0);
313 }
314
315 /**
316  * Attempt to map @p size bytes at @p addr, replacing any existing
317  * @p eio mapping.
318  * 
319  * @param eio   I/O instance state.
320  * @param addr  The address to be mapped.
321  * @param size  The number of bytes to be mapped at @p addr.
322  * 
323  * @retval 0            success
324  * @retval non-zero     if mapping @p addr otherwise fails, a regular
325  *                      unix error code should be returned.
326  */
327 int
328 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
329 {
330         return (eio->map(eio, addr, size));
331 }
332
333 /**
334  * Return the address range mapped by @p eio, if any.
335  * 
336  * @param       eio     I/O instance state.
337  * @param[out]  addr    The address mapped by @p eio.
338  * @param[out]  size    The number of bytes mapped at @p addr.
339  * 
340  * @retval      0       success
341  * @retval      ENXIO   if @p eio has no mapping.
342  */
343 int
344 bhnd_erom_io_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
345     bhnd_size_t *size)
346 {
347         return (eio->tell(eio, addr, size));
348 }
349
350 /**
351  * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
352  * relative to @p eio's current mapping.
353  * 
354  * @param eio           erom I/O callbacks
355  * @param offset        read offset.
356  * @param width         item width (1, 2, or 4 bytes).
357  */
358 uint32_t
359 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
360 {
361         return (eio->read(eio, offset, width));
362 }
363
364 /**
365  * Free all resources held by @p eio.
366  */
367 void
368 bhnd_erom_io_fini(struct bhnd_erom_io *eio)
369 {
370         if (eio->fini != NULL)
371                 return (eio->fini(eio));
372 }
373
374 /**
375  * Allocate, initialize, and return a new I/O instance that will perform
376  * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
377  * 
378  * @param dev   The device to pass to bhnd_alloc_resource() and
379  *              bhnd_release_resource() functions.
380  * @param rid   The resource ID to be used when allocating memory resources.
381  */
382 struct bhnd_erom_io *
383 bhnd_erom_iores_new(device_t dev, int rid)
384 {
385         struct bhnd_erom_iores  *iores;
386
387         iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
388         iores->eio.map = bhnd_erom_iores_map;
389         iores->eio.tell = bhnd_erom_iores_tell;
390         iores->eio.read = bhnd_erom_iores_read;
391         iores->eio.fini = bhnd_erom_iores_fini;
392
393         iores->owner = dev;
394         iores->owner_rid = rid;
395         iores->mapped = NULL;
396         iores->mapped_rid = -1;
397
398         return (&iores->eio);
399 }
400
401 static int
402 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
403     bhnd_size_t size)
404 {
405         struct bhnd_erom_iores *iores;
406
407         iores = (struct bhnd_erom_iores *)eio;
408
409         /* Sanity check the addr/size */
410         if (size == 0)
411                 return (EINVAL);
412
413         if (BHND_ADDR_MAX - size < addr)
414                 return (EINVAL);        /* would overflow */
415
416         /* Check for an existing mapping */
417         if (iores->mapped) {
418                 /* If already mapped, nothing else to do */
419                 if (rman_get_start(iores->mapped->res) == addr &&
420                     rman_get_size(iores->mapped->res) == size)
421                 {
422                         return (0);
423                 }
424
425                 /* Otherwise, we need to drop the existing mapping */
426                 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
427                     iores->mapped_rid, iores->mapped);
428                 iores->mapped = NULL;
429                 iores->mapped_rid = -1;
430         }
431
432         /* Try to allocate the new mapping */
433         iores->mapped_rid = iores->owner_rid;
434         iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
435             &iores->mapped_rid, addr, addr+size-1, size,
436             RF_ACTIVE|RF_SHAREABLE);
437         if (iores->mapped == NULL) {
438                 iores->mapped_rid = -1;
439                 return (ENXIO);
440         }
441
442         return (0);
443 }
444
445 static int
446 bhnd_erom_iores_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
447     bhnd_size_t *size)
448 {
449         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
450
451         if (iores->mapped == NULL)
452                 return (ENXIO);
453
454         *addr = rman_get_start(iores->mapped->res);
455         *size = rman_get_size(iores->mapped->res);
456
457         return (0);
458 }
459
460 static uint32_t
461 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
462 {
463         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
464
465         if (iores->mapped == NULL)
466                 panic("read with invalid mapping");
467
468         switch (width) {
469         case 1:
470                 return (bhnd_bus_read_1(iores->mapped, offset));
471         case 2:
472                 return (bhnd_bus_read_2(iores->mapped, offset));
473         case 4:
474                 return (bhnd_bus_read_4(iores->mapped, offset));
475         default:
476                 panic("invalid width %u", width);
477         }
478 }
479
480 static void
481 bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
482 {
483         struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
484
485         /* Release any mapping */
486         if (iores->mapped) {
487                 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
488                     iores->mapped_rid, iores->mapped);
489                 iores->mapped = NULL;
490                 iores->mapped_rid = -1;
491         }
492
493         free(eio, M_BHND);
494 }
495
496 /**
497  * Initialize an I/O instance that will perform mapping directly from the
498  * given bus space tag and handle.
499  * 
500  * @param iobus The I/O instance to be initialized.
501  * @param addr  The base address mapped by @p bsh.
502  * @param size  The total size mapped by @p bsh.
503  * @param bst   Bus space tag for @p bsh.
504  * @param bsh   Bus space handle mapping the full bus enumeration space.
505  * 
506  * @retval 0            success
507  * @retval non-zero     if initializing @p iobus otherwise fails, a regular
508  *                      unix error code will be returned.
509  */
510 int
511 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
512     bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
513 {
514         iobus->eio.map = bhnd_erom_iobus_map;
515         iobus->eio.tell = bhnd_erom_iobus_tell;
516         iobus->eio.read = bhnd_erom_iobus_read;
517         iobus->eio.fini = NULL;
518
519         iobus->addr = addr;
520         iobus->size = size;
521         iobus->bst = bst;
522         iobus->bsh = bsh;
523         iobus->mapped = false;
524
525         return (0);
526 }
527
528 static int
529 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
530     bhnd_size_t size)
531 {
532         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
533
534         /* Sanity check the addr/size */
535         if (size == 0)
536                 return (EINVAL);
537
538         /* addr+size must not overflow */
539         if (BHND_ADDR_MAX - size < addr)
540                 return (EINVAL);
541
542         /* addr/size must fit within our bus tag's mapping */
543         if (addr < iobus->addr || size > iobus->size)
544                 return (ENXIO);
545
546         if (iobus->size - (addr - iobus->addr) < size)
547                 return (ENXIO);
548
549         /* The new addr offset and size must be representible as a bus_size_t */
550         if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
551                 return (ENXIO);
552
553         if (size > BUS_SPACE_MAXSIZE)
554                 return (ENXIO);
555
556         iobus->offset = addr - iobus->addr;
557         iobus->limit = size;
558         iobus->mapped = true;
559
560         return (0);
561 }
562
563 static int
564 bhnd_erom_iobus_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
565     bhnd_size_t *size)
566 {
567         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
568
569         if (!iobus->mapped)
570                 return (ENXIO);
571
572         *addr = iobus->addr + iobus->offset;
573         *size = iobus->limit;
574
575         return (0);
576 }
577
578 static uint32_t
579 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
580 {
581         struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
582
583         if (!iobus->mapped) 
584                 panic("no active mapping");
585
586         if (iobus->limit < width || iobus->limit - width < offset)
587                 panic("invalid offset %#jx", offset);
588
589         switch (width) {
590         case 1:
591                 return (bus_space_read_1(iobus->bst, iobus->bsh,
592                     iobus->offset + offset));
593         case 2:
594                 return (bus_space_read_2(iobus->bst, iobus->bsh,
595                     iobus->offset + offset));
596         case 4:
597                 return (bus_space_read_4(iobus->bst, iobus->bsh,
598                     iobus->offset + offset));
599         default:
600                 panic("invalid width %u", width);
601         }
602 }