]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/bcma/bcma_erom.c
Import tzdata 2018d
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / bcma / bcma_erom.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2015-2017 Landon Fuller <landonf@landonf.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/kernel.h>
42 #include <sys/limits.h>
43 #include <sys/systm.h>
44
45 #include <machine/bus.h>
46 #include <machine/resource.h>
47
48 #include <dev/bhnd/cores/chipc/chipcreg.h>
49
50 #include "bcma_eromreg.h"
51 #include "bcma_eromvar.h"
52
53 /*
54  * BCMA Enumeration ROM (EROM) Table
55  * 
56  * Provides auto-discovery of BCMA cores on Broadcom's HND SoC.
57  * 
58  * The EROM core address can be found at BCMA_CC_EROM_ADDR within the
59  * ChipCommon registers. The table itself is comprised of 32-bit
60  * type-tagged entries, organized into an array of variable-length
61  * core descriptor records.
62  * 
63  * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF)
64  * marker.
65  */
66
67 static const char       *bcma_erom_entry_type_name (uint8_t entry);
68
69 static int               bcma_erom_read32(struct bcma_erom *erom,
70                              uint32_t *entry);
71 static int               bcma_erom_skip32(struct bcma_erom *erom);
72
73 static int               bcma_erom_skip_core(struct bcma_erom *erom);
74 static int               bcma_erom_skip_mport(struct bcma_erom *erom);
75 static int               bcma_erom_skip_sport_region(struct bcma_erom *erom);
76
77 static int               bcma_erom_seek_next(struct bcma_erom *erom,
78                              uint8_t etype);
79 static int               bcma_erom_region_to_port_type(struct bcma_erom *erom,
80                              uint8_t region_type, bhnd_port_type *port_type);
81
82
83 static int               bcma_erom_peek32(struct bcma_erom *erom,
84                              uint32_t *entry);
85
86 static bus_size_t        bcma_erom_tell(struct bcma_erom *erom);
87 static void              bcma_erom_seek(struct bcma_erom *erom,
88                              bus_size_t offset);
89 static void              bcma_erom_reset(struct bcma_erom *erom);
90
91 static int               bcma_erom_seek_matching_core(struct bcma_erom *sc,
92                              const struct bhnd_core_match *desc,
93                              struct bhnd_core_info *core);
94
95 static int               bcma_erom_parse_core(struct bcma_erom *erom,
96                              struct bcma_erom_core *core);
97
98 static int               bcma_erom_parse_mport(struct bcma_erom *erom,
99                              struct bcma_erom_mport *mport);
100
101 static int               bcma_erom_parse_sport_region(struct bcma_erom *erom,
102                              struct bcma_erom_sport_region *region);
103
104 static void              bcma_erom_to_core_info(const struct bcma_erom_core *core,
105                              u_int core_idx, int core_unit,
106                              struct bhnd_core_info *info);
107
108 /**
109  * BCMA EROM per-instance state.
110  */
111 struct bcma_erom {
112         struct bhnd_erom         obj;
113         device_t                 dev;           /**< parent device, or NULL if none. */
114         struct bhnd_erom_io     *eio;           /**< bus I/O callbacks */
115         bhnd_size_t              offset;        /**< current read offset */
116 };
117
118 #define EROM_LOG(erom, fmt, ...)        do {                    \
119         printf("%s erom[0x%llx]: " fmt, __FUNCTION__,           \
120             (unsigned long long)(erom->offset), ##__VA_ARGS__); \
121 } while(0)
122
123 /** Return the type name for an EROM entry */
124 static const char *
125 bcma_erom_entry_type_name (uint8_t entry)
126 {
127         switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
128         case BCMA_EROM_ENTRY_TYPE_CORE:
129                 return "core";
130         case BCMA_EROM_ENTRY_TYPE_MPORT:
131                 return "mport";
132         case BCMA_EROM_ENTRY_TYPE_REGION:
133                 return "region";
134         default:
135                 return "unknown";
136         }
137 }
138
139 /* BCMA implementation of BHND_EROM_INIT() */
140 static int
141 bcma_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
142     struct bhnd_erom_io *eio)
143 {
144         struct bcma_erom        *sc;
145         bhnd_addr_t              table_addr;
146         int                      error;
147
148         sc = (struct bcma_erom *)erom;
149         sc->eio = eio;
150         sc->offset = 0;
151
152         /* Determine erom table address */
153         if (BHND_ADDR_MAX - BCMA_EROM_TABLE_START < cid->enum_addr)
154                 return (ENXIO); /* would overflow */
155
156         table_addr = cid->enum_addr + BCMA_EROM_TABLE_START;
157
158         /* Try to map the erom table */
159         error = bhnd_erom_io_map(sc->eio, table_addr, BCMA_EROM_TABLE_SIZE);
160         if (error)
161                 return (error);
162
163         return (0);
164 }
165
166 /* BCMA implementation of BHND_EROM_PROBE() */
167 static int
168 bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio,
169     const struct bhnd_chipid *hint, struct bhnd_chipid *cid)
170 {
171         uint32_t idreg, eromptr;
172
173         /* Hints aren't supported; all BCMA devices have a ChipCommon
174          * core */
175         if (hint != NULL)
176                 return (EINVAL);
177
178         /* Confirm CHIPC_EROMPTR availability */        
179         idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);
180         if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS)))
181                 return (ENXIO);
182
183         /* Fetch EROM address */
184         eromptr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);
185
186         /* Parse chip identifier */
187         *cid = bhnd_parse_chipid(idreg, eromptr);
188
189         /* Verify chip type */
190         switch (cid->chip_type) {
191                 case BHND_CHIPTYPE_BCMA:
192                         return (BUS_PROBE_DEFAULT);
193
194                 case BHND_CHIPTYPE_BCMA_ALT:
195                 case BHND_CHIPTYPE_UBUS:
196                         return (BUS_PROBE_GENERIC);
197
198                 default:
199                         return (ENXIO);
200         }       
201 }
202
203 static void
204 bcma_erom_fini(bhnd_erom_t *erom)
205 {
206         struct bcma_erom *sc = (struct bcma_erom *)erom;
207
208         bhnd_erom_io_fini(sc->eio);
209 }
210
211 static int
212 bcma_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
213     struct bhnd_core_info *core)
214 {
215         struct bcma_erom *sc = (struct bcma_erom *)erom;
216
217         /* Search for the first matching core */
218         return (bcma_erom_seek_matching_core(sc, desc, core));
219 }
220
221 static int
222 bcma_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
223     bhnd_port_type port_type, u_int port_num, u_int region_num,
224     struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size)
225 {
226         struct bcma_erom        *sc;
227         struct bcma_erom_core    ec;
228         uint32_t                 entry;
229         uint8_t                  region_port, region_type;
230         bool                     found;
231         int                      error;
232
233         sc = (struct bcma_erom *)erom;
234
235         /* Seek to the first matching core and provide the core info
236          * to the caller */
237         if ((error = bcma_erom_seek_matching_core(sc, desc, core)))
238                 return (error);
239
240         if ((error = bcma_erom_parse_core(sc, &ec)))
241                 return (error);
242
243         /* Skip master ports */
244         for (u_long i = 0; i < ec.num_mport; i++) {
245                 if ((error = bcma_erom_skip_mport(sc)))
246                         return (error);
247         }
248
249         /* Seek to the region block for the given port type */
250         found = false;
251         while (1) {
252                 bhnd_port_type  p_type;
253                 uint8_t         r_type;
254
255                 if ((error = bcma_erom_peek32(sc, &entry)))
256                         return (error);
257
258                 if (!BCMA_EROM_ENTRY_IS(entry, REGION))
259                         return (ENOENT);
260
261                 /* Expected region type? */
262                 r_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
263                 error = bcma_erom_region_to_port_type(sc, r_type, &p_type);
264                 if (error)
265                         return (error);
266
267                 if (p_type == port_type) {
268                         found = true;
269                         break;
270                 }
271
272                 /* Skip to next entry */
273                 if ((error = bcma_erom_skip_sport_region(sc)))
274                         return (error);
275         }
276
277         if (!found)
278                 return (ENOENT);
279
280         /* Found the appropriate port type block; now find the region records
281          * for the given port number */
282         found = false;
283         for (u_int i = 0; i <= port_num; i++) {
284                 bhnd_port_type  p_type;
285
286                 if ((error = bcma_erom_peek32(sc, &entry)))
287                         return (error);
288                 
289                 if (!BCMA_EROM_ENTRY_IS(entry, REGION))
290                         return (ENOENT);
291
292                 /* Fetch the type/port of the first region entry */
293                 region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
294                 region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
295
296                 /* Have we found the region entries for the desired port? */
297                 if (i == port_num) {
298                         error = bcma_erom_region_to_port_type(sc, region_type,
299                             &p_type);
300                         if (error)
301                                 return (error);
302
303                         if (p_type == port_type)
304                                 found = true;
305
306                         break;
307                 }
308
309                 /* Otherwise, seek to next block of region records */
310                 while (1) {
311                         uint8_t next_type, next_port;
312         
313                         if ((error = bcma_erom_skip_sport_region(sc)))
314                                 return (error);
315
316                         if ((error = bcma_erom_peek32(sc, &entry)))
317                                 return (error);
318
319                         if (!BCMA_EROM_ENTRY_IS(entry, REGION))
320                                 return (ENOENT);
321
322                         next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
323                         next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
324
325                         if (next_type != region_type ||
326                             next_port != region_port)
327                                 break;
328                 }
329         }
330
331         if (!found)
332                 return (ENOENT);
333
334         /* Finally, search for the requested region number */
335         for (u_int i = 0; i <= region_num; i++) {
336                 struct bcma_erom_sport_region   region;
337                 uint8_t                         next_port, next_type;
338
339                 if ((error = bcma_erom_peek32(sc, &entry)))
340                         return (error);
341                 
342                 if (!BCMA_EROM_ENTRY_IS(entry, REGION))
343                         return (ENOENT);
344
345                 /* Check for the end of the region block */
346                 next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
347                 next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
348
349                 if (next_type != region_type ||
350                     next_port != region_port)
351                         break;
352
353                 /* Parse the region */
354                 if ((error = bcma_erom_parse_sport_region(sc, &region)))
355                         return (error);
356
357                 /* Is this our target region_num? */
358                 if (i == region_num) {
359                         /* Found */
360                         *addr = region.base_addr;
361                         *size = region.size;
362                         return (0);
363                 }
364         }
365
366         /* Not found */
367         return (ENOENT);
368 };
369
370 static int
371 bcma_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
372     u_int *num_cores)
373 {
374         struct bcma_erom        *sc;
375         struct bhnd_core_info   *buffer;
376         bus_size_t               initial_offset;
377         u_int                    count;
378         int                      error;
379
380         sc = (struct bcma_erom *)erom;
381
382         buffer = NULL;
383         initial_offset = bcma_erom_tell(sc);
384
385         /* Determine the core count */
386         bcma_erom_reset(sc);
387         for (count = 0, error = 0; !error; count++) {
388                 struct bcma_erom_core core;
389
390                 /* Seek to the first readable core entry */
391                 error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE);
392                 if (error == ENOENT)
393                         break;
394                 else if (error)
395                         goto cleanup;
396                 
397                 /* Read past the core descriptor */
398                 if ((error = bcma_erom_parse_core(sc, &core)))
399                         goto cleanup;
400         }
401
402         /* Allocate our output buffer */
403         buffer = mallocarray(count, sizeof(struct bhnd_core_info), M_BHND,
404             M_NOWAIT);
405         if (buffer == NULL) {
406                 error = ENOMEM;
407                 goto cleanup;
408         }
409
410         /* Parse all core descriptors */
411         bcma_erom_reset(sc);
412         for (u_int i = 0; i < count; i++) {
413                 struct bcma_erom_core   core;
414                 int                     unit;
415
416                 /* Parse the core */
417                 error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE);
418                 if (error)
419                         goto cleanup;
420
421                 error = bcma_erom_parse_core(sc, &core);
422                 if (error)
423                         goto cleanup;
424
425                 /* Determine the unit number */
426                 unit = 0;
427                 for (u_int j = 0; j < i; j++) {
428                         if (buffer[i].vendor == buffer[j].vendor &&
429                             buffer[i].device == buffer[j].device)
430                                 unit++;
431                 }
432
433                 /* Convert to a bhnd info record */
434                 bcma_erom_to_core_info(&core, i, unit, &buffer[i]);
435         }
436
437 cleanup:
438         if (!error) {
439                 *cores = buffer;
440                 *num_cores = count;
441         } else {
442                 if (buffer != NULL)
443                         free(buffer, M_BHND);
444         }
445
446         /* Restore the initial position */
447         bcma_erom_seek(sc, initial_offset);
448         return (error);
449 }
450
451 static void
452 bcma_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
453 {
454         free(cores, M_BHND);
455 }
456
457 /**
458  * Return the current read position.
459  */
460 static bus_size_t
461 bcma_erom_tell(struct bcma_erom *erom)
462 {
463         return (erom->offset);
464 }
465
466 /**
467  * Seek to an absolute read position.
468  */
469 static void
470 bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset)
471 {
472         erom->offset = offset;
473 }
474
475 /**
476  * Read a 32-bit entry value from the EROM table without advancing the
477  * read position.
478  * 
479  * @param erom EROM read state.
480  * @param entry Will contain the read result on success.
481  * @retval 0 success
482  * @retval ENOENT The end of the EROM table was reached.
483  * @retval non-zero The read could not be completed.
484  */
485 static int
486 bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry)
487 {
488         if (erom->offset >= (BCMA_EROM_TABLE_SIZE - sizeof(uint32_t))) {
489                 EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n");
490                 return (EINVAL);
491         }
492
493         *entry = bhnd_erom_io_read(erom->eio, erom->offset, 4);
494         return (0);
495 }
496
497 /**
498  * Read a 32-bit entry value from the EROM table.
499  * 
500  * @param erom EROM read state.
501  * @param entry Will contain the read result on success.
502  * @retval 0 success
503  * @retval ENOENT The end of the EROM table was reached.
504  * @retval non-zero The read could not be completed.
505  */
506 static int
507 bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry)
508 {
509         int error;
510
511         if ((error = bcma_erom_peek32(erom, entry)) == 0)
512                 erom->offset += 4;
513
514         return (error);
515 }
516
517 /**
518  * Read and discard 32-bit entry value from the EROM table.
519  * 
520  * @param erom EROM read state.
521  * @retval 0 success
522  * @retval ENOENT The end of the EROM table was reached.
523  * @retval non-zero The read could not be completed.
524  */
525 static int
526 bcma_erom_skip32(struct bcma_erom *erom)
527 {
528         uint32_t        entry;
529
530         return bcma_erom_read32(erom, &entry);
531 }
532
533 /**
534  * Read and discard a core descriptor from the EROM table.
535  * 
536  * @param erom EROM read state.
537  * @retval 0 success
538  * @retval ENOENT The end of the EROM table was reached.
539  * @retval non-zero The read could not be completed.
540  */
541 static int
542 bcma_erom_skip_core(struct bcma_erom *erom)
543 {
544         struct bcma_erom_core core;
545         return (bcma_erom_parse_core(erom, &core));
546 }
547
548 /**
549  * Read and discard a master port descriptor from the EROM table.
550  * 
551  * @param erom EROM read state.
552  * @retval 0 success
553  * @retval ENOENT The end of the EROM table was reached.
554  * @retval non-zero The read could not be completed.
555  */
556 static int
557 bcma_erom_skip_mport(struct bcma_erom *erom)
558 {
559         struct bcma_erom_mport mp;
560         return (bcma_erom_parse_mport(erom, &mp));
561 }
562
563 /**
564  * Read and discard a port region descriptor from the EROM table.
565  * 
566  * @param erom EROM read state.
567  * @retval 0 success
568  * @retval ENOENT The end of the EROM table was reached.
569  * @retval non-zero The read could not be completed.
570  */
571 static int
572 bcma_erom_skip_sport_region(struct bcma_erom *erom)
573 {
574         struct bcma_erom_sport_region r;
575         return (bcma_erom_parse_sport_region(erom, &r));
576 }
577
578 /**
579  * Seek to the next entry matching the given EROM entry type.
580  * 
581  * @param erom EROM read state.
582  * @param etype  One of BCMA_EROM_ENTRY_TYPE_CORE,
583  * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION.
584  * @retval 0 success
585  * @retval ENOENT The end of the EROM table was reached.
586  * @retval non-zero Reading or parsing the descriptor failed.
587  */
588 static int
589 bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype)
590 {
591         uint32_t                        entry;
592         int                             error;
593
594         /* Iterate until we hit an entry matching the requested type. */
595         while (!(error = bcma_erom_peek32(erom, &entry))) {
596                 /* Handle EOF */
597                 if (entry == BCMA_EROM_TABLE_EOF)
598                         return (ENOENT);
599
600                 /* Invalid entry */
601                 if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID))
602                         return (EINVAL);
603
604                 /* Entry type matches? */
605                 if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype)
606                         return (0);
607
608                 /* Skip non-matching entry types. */
609                 switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
610                 case BCMA_EROM_ENTRY_TYPE_CORE:
611                         if ((error = bcma_erom_skip_core(erom)))
612                                 return (error);
613
614                         break;
615
616                 case BCMA_EROM_ENTRY_TYPE_MPORT:
617                         if ((error = bcma_erom_skip_mport(erom)))
618                                 return (error);
619
620                         break;
621                 
622                 case BCMA_EROM_ENTRY_TYPE_REGION:
623                         if ((error = bcma_erom_skip_sport_region(erom)))
624                                 return (error);
625                         break;
626
627                 default:
628                         /* Unknown entry type! */
629                         return (EINVAL);        
630                 }
631         }
632
633         return (error);
634 }
635
636 /**
637  * Return the read position to the start of the EROM table.
638  * 
639  * @param erom EROM read state.
640  */
641 static void
642 bcma_erom_reset(struct bcma_erom *erom)
643 {
644         erom->offset = 0;
645 }
646
647 /**
648  * Seek to the first core entry matching @p desc.
649  * 
650  * @param erom EROM read state.
651  * @param desc The core match descriptor.
652  * @param[out] core On success, the matching core info. If the core info
653  * is not desired, a NULL pointer may be provided.
654  * @retval 0 success
655  * @retval ENOENT The end of the EROM table was reached before @p index was
656  * found.
657  * @retval non-zero Reading or parsing failed.
658  */
659 static int
660 bcma_erom_seek_matching_core(struct bcma_erom *sc,
661     const struct bhnd_core_match *desc, struct bhnd_core_info *core)
662 {
663         struct bhnd_core_match   imatch;
664         bus_size_t               core_offset, next_offset;
665         int                      error;
666
667         /* Seek to table start. */
668         bcma_erom_reset(sc);
669
670         /* We can't determine a core's unit number during the initial scan. */
671         imatch = *desc;
672         imatch.m.match.core_unit = 0;
673
674         /* Locate the first matching core */
675         for (u_int i = 0; i < UINT_MAX; i++) {
676                 struct bcma_erom_core   ec;
677                 struct bhnd_core_info   ci;
678
679                 /* Seek to the next core */
680                 error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE);
681                 if (error)
682                         return (error);
683
684                 /* Save the core offset */
685                 core_offset = bcma_erom_tell(sc);
686         
687                 /* Parse the core */
688                 if ((error = bcma_erom_parse_core(sc, &ec)))
689                         return (error);
690
691                 bcma_erom_to_core_info(&ec, i, 0, &ci);
692
693                 /* Check for initial match */
694                 if (!bhnd_core_matches(&ci, &imatch))
695                         continue;
696
697                 /* Re-scan preceding cores to determine the unit number. */
698                 next_offset = bcma_erom_tell(sc);
699                 bcma_erom_reset(sc);
700                 for (u_int j = 0; j < i; j++) {
701                         /* Parse the core */
702                         error = bcma_erom_seek_next(sc,
703                             BCMA_EROM_ENTRY_TYPE_CORE);
704                         if (error)
705                                 return (error);
706                         
707                         if ((error = bcma_erom_parse_core(sc, &ec)))
708                                 return (error);
709
710                         /* Bump the unit number? */
711                         if (ec.vendor == ci.vendor && ec.device == ci.device)
712                                 ci.unit++;
713                 }
714
715                 /* Check for full match against now-valid unit number */
716                 if (!bhnd_core_matches(&ci, desc)) {
717                         /* Reposition to allow reading the next core */
718                         bcma_erom_seek(sc, next_offset);
719                         continue;
720                 }
721
722                 /* Found; seek to the core's initial offset and provide
723                  * the core info to the caller */
724                 bcma_erom_seek(sc, core_offset);
725                 if (core != NULL)
726                         *core = ci;
727
728                 return (0);
729         }
730
731         /* Not found, or a parse error occured */
732         return (error);
733 }
734
735 /**
736  * Read the next core descriptor from the EROM table.
737  * 
738  * @param erom EROM read state.
739  * @param[out] core On success, will be populated with the parsed core
740  * descriptor data.
741  * @retval 0 success
742  * @retval ENOENT The end of the EROM table was reached.
743  * @retval non-zero Reading or parsing the core descriptor failed.
744  */
745 static int
746 bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core)
747 {
748         uint32_t        entry;
749         int             error;
750
751         /* Parse CoreDescA */
752         if ((error = bcma_erom_read32(erom, &entry)))
753                 return (error);
754         
755         /* Handle EOF */
756         if (entry == BCMA_EROM_TABLE_EOF)
757                 return (ENOENT);
758         
759         if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
760                 EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n",
761                    entry, bcma_erom_entry_type_name(entry));
762                 
763                 return (EINVAL);
764         }
765
766         core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER);
767         core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID);
768         
769         /* Parse CoreDescB */
770         if ((error = bcma_erom_read32(erom, &entry)))
771                 return (error);
772
773         if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
774                 return (EINVAL);
775         }
776
777         core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV);
778         core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP);
779         core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP);
780         core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP);
781         core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP);
782
783         return (0);
784 }
785
786 /**
787  * Read the next master port descriptor from the EROM table.
788  * 
789  * @param erom EROM read state.
790  * @param[out] mport On success, will be populated with the parsed
791  * descriptor data.
792  * @retval 0 success
793  * @retval non-zero Reading or parsing the descriptor failed.
794  */
795 static int
796 bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport)
797 {
798         uint32_t        entry;
799         int             error;
800
801         /* Parse the master port descriptor */
802         if ((error = bcma_erom_read32(erom, &entry)))
803                 return (error);
804         
805         if (!BCMA_EROM_ENTRY_IS(entry, MPORT))
806                 return (EINVAL);
807
808         mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID);
809         mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM);
810
811         return (0);
812 }
813
814 /**
815  * Read the next slave port region descriptor from the EROM table.
816  * 
817  * @param erom EROM read state.
818  * @param[out] mport On success, will be populated with the parsed
819  * descriptor data.
820  * @retval 0 success
821  * @retval ENOENT The end of the region descriptor table was reached.
822  * @retval non-zero Reading or parsing the descriptor failed.
823  */
824 static int
825 bcma_erom_parse_sport_region(struct bcma_erom *erom,
826     struct bcma_erom_sport_region *region)
827 {
828         uint32_t        entry;
829         uint8_t         size_type;
830         int             error;
831
832         /* Peek at the region descriptor */
833         if (bcma_erom_peek32(erom, &entry))
834                 return (EINVAL);
835
836         /* A non-region entry signals the end of the region table */
837         if (!BCMA_EROM_ENTRY_IS(entry, REGION)) {
838                 return (ENOENT);
839         } else {
840                 bcma_erom_skip32(erom);
841         }
842
843         region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE);
844         region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
845         region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
846         size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE);
847
848         /* If region address is 64-bit, fetch the high bits. */
849         if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) {
850                 if ((error = bcma_erom_read32(erom, &entry)))
851                         return (error);
852                 
853                 region->base_addr |= ((bhnd_addr_t) entry << 32);
854         }
855
856         /* Parse the region size; it's either encoded as the binary logarithm
857          * of the number of 4K pages (i.e. log2 n), or its encoded as a
858          * 32-bit/64-bit literal value directly following the current entry. */
859         if (size_type == BCMA_EROM_REGION_SIZE_OTHER) {
860                 if ((error = bcma_erom_read32(erom, &entry)))
861                         return (error);
862
863                 region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL);
864
865                 if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) {
866                         if ((error = bcma_erom_read32(erom, &entry)))
867                                 return (error);
868                         region->size |= ((bhnd_size_t) entry << 32);
869                 }
870         } else {
871                 region->size = BCMA_EROM_REGION_SIZE_BASE << size_type;
872         }
873
874         /* Verify that addr+size does not overflow. */
875         if (region->size != 0 &&
876             BHND_ADDR_MAX - (region->size - 1) < region->base_addr)
877         {
878                 EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n",
879                     bcma_erom_entry_type_name(region->region_type),
880                     region->region_port,
881                     (unsigned long long) region->base_addr,
882                     (unsigned long long) region->size);
883
884                 return (EINVAL);
885         }
886
887         return (0);
888 }
889
890 /**
891  * Convert a bcma_erom_core record to its bhnd_core_info representation.
892  * 
893  * @param core EROM core record to convert.
894  * @param core_idx The core index of @p core.
895  * @param core_unit The core unit of @p core.
896  * @param[out] info The populated bhnd_core_info representation.
897  */
898 static void
899 bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx,
900     int core_unit, struct bhnd_core_info *info)
901 {
902         info->vendor = core->vendor;
903         info->device = core->device;
904         info->hwrev = core->rev;
905         info->core_idx = core_idx;
906         info->unit = core_unit;
907 }
908
909 /**
910  * Map an EROM region type to its corresponding port type.
911  * 
912  * @param region_type Region type value.
913  * @param[out] port_type On success, the corresponding port type.
914  */
915 static int
916 bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type,
917     bhnd_port_type *port_type)
918 {
919         switch (region_type) {
920         case BCMA_EROM_REGION_TYPE_DEVICE:
921                 *port_type = BHND_PORT_DEVICE;
922                 return (0);
923         case BCMA_EROM_REGION_TYPE_BRIDGE:
924                 *port_type = BHND_PORT_BRIDGE;
925                 return (0);
926         case BCMA_EROM_REGION_TYPE_MWRAP:
927         case BCMA_EROM_REGION_TYPE_SWRAP:
928                 *port_type = BHND_PORT_AGENT;
929                 return (0);
930         default:
931                 EROM_LOG(erom, "unsupported region type %hhx\n",
932                         region_type);
933                 return (EINVAL);
934         }
935 }
936
937 /**
938  * Register all MMIO region descriptors for the given slave port.
939  * 
940  * @param erom EROM read state.
941  * @param corecfg Core info to be populated with the scanned port regions.
942  * @param port_num Port index for which regions will be parsed.
943  * @param region_type The region type to be parsed.
944  * @param[out] offset The offset at which to perform parsing. On success, this
945  * will be updated to point to the next EROM table entry.
946  */
947 static int 
948 bcma_erom_corecfg_fill_port_regions(struct bcma_erom *erom,
949     struct bcma_corecfg *corecfg, bcma_pid_t port_num,
950     uint8_t region_type)
951 {
952         struct bcma_sport       *sport;
953         struct bcma_sport_list  *sports;
954         bus_size_t               entry_offset;
955         int                      error;
956         bhnd_port_type           port_type;
957
958         error = 0;
959
960         /* Determine the port type for this region type. */
961         error = bcma_erom_region_to_port_type(erom, region_type, &port_type);
962         if (error)
963                 return (error);
964
965         /* Fetch the list to be populated */
966         sports = bcma_corecfg_get_port_list(corecfg, port_type);
967         
968         /* Allocate a new port descriptor */
969         sport = bcma_alloc_sport(port_num, port_type);
970         if (sport == NULL)
971                 return (ENOMEM);
972
973         /* Read all address regions defined for this port */
974         for (bcma_rmid_t region_num = 0;; region_num++) {
975                 struct bcma_map                 *map;
976                 struct bcma_erom_sport_region    spr;
977
978                 /* No valid port definition should come anywhere near
979                  * BCMA_RMID_MAX. */
980                 if (region_num == BCMA_RMID_MAX) {
981                         EROM_LOG(erom, "core%u %s%u: region count reached "
982                             "upper limit of %u\n",
983                             corecfg->core_info.core_idx,
984                             bhnd_port_type_name(port_type),
985                             port_num, BCMA_RMID_MAX);
986
987                         error = EINVAL;
988                         goto cleanup;
989                 }
990
991                 /* Parse the next region entry. */
992                 entry_offset = bcma_erom_tell(erom);
993                 error = bcma_erom_parse_sport_region(erom, &spr);
994                 if (error && error != ENOENT) {
995                         EROM_LOG(erom, "core%u %s%u.%u: invalid slave port "
996                             "address region\n",
997                             corecfg->core_info.core_idx,
998                             bhnd_port_type_name(port_type),
999                             port_num, region_num);
1000                         goto cleanup;
1001                 }
1002
1003                 /* ENOENT signals no further region entries */
1004                 if (error == ENOENT) {
1005                         /* No further entries */
1006                         error = 0;
1007                         break;
1008                 } 
1009                 
1010                 /* A region or type mismatch also signals no further region
1011                  * entries */
1012                 if (spr.region_port != port_num ||
1013                     spr.region_type != region_type)
1014                 {
1015                         /* We don't want to consume this entry */
1016                         bcma_erom_seek(erom, entry_offset);
1017
1018                         error = 0;
1019                         goto cleanup;
1020                 }
1021
1022                 /*
1023                  * Create the map entry. 
1024                  */
1025                 map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT);
1026                 if (map == NULL) {
1027                         error = ENOMEM;
1028                         goto cleanup;
1029                 }
1030
1031                 map->m_region_num = region_num;
1032                 map->m_base = spr.base_addr;
1033                 map->m_size = spr.size;
1034                 map->m_rid = -1;
1035
1036                 /* Add the region map to the port */
1037                 STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link);
1038                 sport->sp_num_maps++;
1039         }
1040
1041 cleanup:
1042         /* Append the new port descriptor on success, or deallocate the
1043          * partially parsed descriptor on failure. */
1044         if (error == 0) {
1045                 STAILQ_INSERT_TAIL(sports, sport, sp_link);
1046         } else if (sport != NULL) {
1047                 bcma_free_sport(sport);
1048         }
1049
1050         return error;
1051 }
1052
1053 /**
1054  * Parse the next core entry from the EROM table and produce a bcma_corecfg
1055  * to be owned by the caller.
1056  * 
1057  * @param erom A bcma EROM instance.
1058  * @param[out] result On success, the core's device info. The caller inherits
1059  * ownership of this allocation.
1060  * 
1061  * @return If successful, returns 0. If the end of the EROM table is hit,
1062  * ENOENT will be returned. On error, returns a non-zero error value.
1063  */
1064 int
1065 bcma_erom_next_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result)
1066 {
1067         struct bcma_corecfg     *cfg;
1068         struct bcma_erom_core    core;
1069         uint8_t                  first_region_type;
1070         bus_size_t               initial_offset;
1071         u_int                    core_index;
1072         int                      core_unit;
1073         int                      error;
1074
1075         cfg = NULL;
1076         initial_offset = bcma_erom_tell(erom);
1077
1078         /* Parse the next core entry */
1079         if ((error = bcma_erom_parse_core(erom, &core)))
1080                 return (error);
1081
1082         /* Determine the core's index and unit numbers */
1083         bcma_erom_reset(erom);
1084         core_unit = 0;
1085         core_index = 0;
1086         for (; bcma_erom_tell(erom) != initial_offset; core_index++) {
1087                 struct bcma_erom_core prev_core;
1088
1089                 /* Parse next core */
1090                 error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
1091                 if (error)
1092                         return (error);
1093
1094                 if ((error = bcma_erom_parse_core(erom, &prev_core)))
1095                         return (error);
1096
1097                 /* Is earlier unit? */
1098                 if (core.vendor == prev_core.vendor &&
1099                     core.device == prev_core.device)
1100                 {
1101                         core_unit++;
1102                 }
1103
1104                 /* Seek to next core */
1105                 error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
1106                 if (error)
1107                         return (error);
1108         }
1109
1110         /* We already parsed the core descriptor */
1111         if ((error = bcma_erom_skip_core(erom)))
1112                 return (error);
1113
1114         /* Allocate our corecfg */
1115         cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor,
1116             core.device, core.rev);
1117         if (cfg == NULL)
1118                 return (ENOMEM);
1119         
1120         /* These are 5-bit values in the EROM table, and should never be able
1121          * to overflow BCMA_PID_MAX. */
1122         KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count"));
1123         KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count"));
1124         KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX,
1125             ("unsupported wport count"));
1126
1127         if (bootverbose) {
1128                 EROM_LOG(erom, 
1129                     "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n",
1130                     core_index,
1131                     bhnd_vendor_name(core.vendor),
1132                     bhnd_find_core_name(core.vendor, core.device), 
1133                     core.device, core.rev, core_unit);
1134         }
1135
1136         cfg->num_master_ports = core.num_mport;
1137         cfg->num_dev_ports = 0;         /* determined below */
1138         cfg->num_bridge_ports = 0;      /* determined blow */
1139         cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap;
1140
1141         /* Parse Master Port Descriptors */
1142         for (uint8_t i = 0; i < core.num_mport; i++) {
1143                 struct bcma_mport       *mport;
1144                 struct bcma_erom_mport   mpd;
1145         
1146                 /* Parse the master port descriptor */
1147                 error = bcma_erom_parse_mport(erom, &mpd);
1148                 if (error)
1149                         goto failed;
1150
1151                 /* Initialize a new bus mport structure */
1152                 mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT);
1153                 if (mport == NULL) {
1154                         error = ENOMEM;
1155                         goto failed;
1156                 }
1157                 
1158                 mport->mp_vid = mpd.port_vid;
1159                 mport->mp_num = mpd.port_num;
1160
1161                 /* Update dinfo */
1162                 STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link);
1163         }
1164         
1165
1166         /*
1167          * Determine whether this is a bridge device; if so, we can
1168          * expect the first sequence of address region descriptors to
1169          * be of EROM_REGION_TYPE_BRIDGE instead of
1170          * BCMA_EROM_REGION_TYPE_DEVICE.
1171          * 
1172          * It's unclear whether this is the correct mechanism by which we
1173          * should detect/handle bridge devices, but this approach matches
1174          * that of (some of) Broadcom's published drivers.
1175          */
1176         if (core.num_dport > 0) {
1177                 uint32_t entry;
1178
1179                 if ((error = bcma_erom_peek32(erom, &entry)))
1180                         goto failed;
1181
1182                 if (BCMA_EROM_ENTRY_IS(entry, REGION) && 
1183                     BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE)
1184                 {
1185                         first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE;
1186                         cfg->num_dev_ports = 0;
1187                         cfg->num_bridge_ports = core.num_dport;
1188                 } else {
1189                         first_region_type = BCMA_EROM_REGION_TYPE_DEVICE;
1190                         cfg->num_dev_ports = core.num_dport;
1191                         cfg->num_bridge_ports = 0;
1192                 }
1193         }
1194         
1195         /* Device/bridge port descriptors */
1196         for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) {
1197                 error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num,
1198                     first_region_type);
1199
1200                 if (error)
1201                         goto failed;
1202         }
1203
1204         /* Wrapper (aka device management) descriptors (for master ports). */
1205         for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) {
1206                 error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num,
1207                     BCMA_EROM_REGION_TYPE_MWRAP);
1208
1209                 if (error)
1210                         goto failed;
1211         }
1212
1213         
1214         /* Wrapper (aka device management) descriptors (for slave ports). */    
1215         for (uint8_t i = 0; i < core.num_swrap; i++) {
1216                 /* Slave wrapper ports are not numbered distinctly from master
1217                  * wrapper ports. */
1218
1219                 /* 
1220                  * Broadcom DDR1/DDR2 Memory Controller
1221                  * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) ->
1222                  * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2)
1223                  *
1224                  * ARM BP135 AMBA3 AXI to APB Bridge
1225                  * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) ->
1226                  * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2)
1227                  *
1228                  * core.num_mwrap
1229                  * ===>
1230                  * (core.num_mwrap > 0) ?
1231                  *           core.num_mwrap :
1232                  *           ((core.vendor == BHND_MFGID_BCM) ? 1 : 0)
1233                  */
1234                 uint8_t sp_num;
1235                 sp_num = (core.num_mwrap > 0) ?
1236                                 core.num_mwrap :
1237                                 ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i;
1238                 error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num,
1239                     BCMA_EROM_REGION_TYPE_SWRAP);
1240
1241                 if (error)
1242                         goto failed;
1243         }
1244
1245         /*
1246          * Seek to the next core entry (if any), skipping any dangling/invalid
1247          * region entries.
1248          * 
1249          * On the BCM4706, the EROM entry for the memory controller core
1250          * (0x4bf/0x52E) contains a dangling/unused slave wrapper port region
1251          * descriptor.
1252          */
1253         if ((error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE))) {
1254                 if (error != ENOENT)
1255                         goto failed;
1256         }
1257
1258         *result = cfg;
1259         return (0);
1260         
1261 failed:
1262         if (cfg != NULL)
1263                 bcma_free_corecfg(cfg);
1264
1265         return error;
1266 }
1267
1268 static int
1269 bcma_erom_dump(bhnd_erom_t *erom)
1270 {
1271         struct bcma_erom        *sc;
1272         uint32_t                entry;
1273         int                     error;
1274
1275         sc = (struct bcma_erom *)erom;
1276
1277         bcma_erom_reset(sc);
1278
1279         while (!(error = bcma_erom_read32(sc, &entry))) {
1280                 /* Handle EOF */
1281                 if (entry == BCMA_EROM_TABLE_EOF) {
1282                         EROM_LOG(sc, "EOF\n");
1283                         return (0);
1284                 }
1285
1286                 /* Invalid entry */
1287                 if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) {
1288                         EROM_LOG(sc, "invalid EROM entry %#x\n", entry);
1289                         return (EINVAL);
1290                 }
1291
1292                 switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
1293                 case BCMA_EROM_ENTRY_TYPE_CORE: {
1294                         /* CoreDescA */
1295                         EROM_LOG(sc, "coreA (0x%x)\n", entry);
1296                         EROM_LOG(sc, "\tdesigner:\t0x%x\n",
1297                             BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER));
1298                         EROM_LOG(sc, "\tid:\t\t0x%x\n",
1299                             BCMA_EROM_GET_ATTR(entry, COREA_ID));
1300                         EROM_LOG(sc, "\tclass:\t\t0x%x\n",
1301                             BCMA_EROM_GET_ATTR(entry, COREA_CLASS));
1302
1303                         /* CoreDescB */
1304                         if ((error = bcma_erom_read32(sc, &entry))) {
1305                                 EROM_LOG(sc, "error reading CoreDescB: %d\n",
1306                                     error);
1307                                 return (error);
1308                         }
1309
1310                         if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
1311                                 EROM_LOG(sc, "invalid core descriptor; found "
1312                                     "unexpected entry %#x (type=%s)\n",
1313                                     entry, bcma_erom_entry_type_name(entry));
1314                                 return (EINVAL);
1315                         }
1316
1317                         EROM_LOG(sc, "coreB (0x%x)\n", entry);
1318                         EROM_LOG(sc, "\trev:\t0x%x\n",
1319                             BCMA_EROM_GET_ATTR(entry, COREB_REV));
1320                         EROM_LOG(sc, "\tnummp:\t0x%x\n",
1321                             BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP));
1322                         EROM_LOG(sc, "\tnumdp:\t0x%x\n",
1323                             BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP));
1324                         EROM_LOG(sc, "\tnumwmp:\t0x%x\n",
1325                             BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP));
1326                         EROM_LOG(sc, "\tnumwsp:\t0x%x\n",
1327                             BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP));
1328
1329                         break;
1330                 }
1331                 case BCMA_EROM_ENTRY_TYPE_MPORT:
1332                         EROM_LOG(sc, "\tmport 0x%x\n", entry);
1333                         EROM_LOG(sc, "\t\tport:\t0x%x\n",
1334                             BCMA_EROM_GET_ATTR(entry, MPORT_NUM));
1335                         EROM_LOG(sc, "\t\tid:\t\t0x%x\n",
1336                             BCMA_EROM_GET_ATTR(entry, MPORT_ID));
1337                         break;
1338
1339                 case BCMA_EROM_ENTRY_TYPE_REGION: {
1340                         bool    addr64;
1341                         uint8_t size_type;
1342
1343                         addr64 = (BCMA_EROM_GET_ATTR(entry, REGION_64BIT) != 0);
1344                         size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE);
1345
1346                         EROM_LOG(sc, "\tregion 0x%x:\n", entry);
1347                         EROM_LOG(sc, "\t\t%s:\t0x%x\n",
1348                             addr64 ? "baselo" : "base",
1349                             BCMA_EROM_GET_ATTR(entry, REGION_BASE));
1350                         EROM_LOG(sc, "\t\tport:\t0x%x\n",
1351                             BCMA_EROM_GET_ATTR(entry, REGION_PORT));
1352                         EROM_LOG(sc, "\t\ttype:\t0x%x\n",
1353                             BCMA_EROM_GET_ATTR(entry, REGION_TYPE));
1354                         EROM_LOG(sc, "\t\tsztype:\t0x%hhx\n", size_type);
1355
1356                         /* Read the base address high bits */
1357                         if (addr64) {
1358                                 if ((error = bcma_erom_read32(sc, &entry))) {
1359                                         EROM_LOG(sc, "error reading region "
1360                                             "base address high bits %d\n",
1361                                             error);
1362                                         return (error);
1363                                 }
1364
1365                                 EROM_LOG(sc, "\t\tbasehi:\t0x%x\n", entry);
1366                         }
1367
1368                         /* Read extended size descriptor */
1369                         if (size_type == BCMA_EROM_REGION_SIZE_OTHER) {
1370                                 bool size64;
1371
1372                                 if ((error = bcma_erom_read32(sc, &entry))) {
1373                                         EROM_LOG(sc, "error reading region "
1374                                             "size descriptor %d\n",
1375                                             error);
1376                                         return (error);
1377                                 }
1378
1379                                 if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT))
1380                                         size64 = true;
1381                                 else
1382                                         size64 = false;
1383
1384                                 EROM_LOG(sc, "\t\t%s:\t0x%x\n",
1385                                     size64 ? "sizelo" : "size",
1386                                     BCMA_EROM_GET_ATTR(entry, RSIZE_VAL));
1387
1388                                 if (size64) {
1389                                         error = bcma_erom_read32(sc, &entry);
1390                                         if (error) {
1391                                                 EROM_LOG(sc, "error reading "
1392                                                     "region size high bits: "
1393                                                     "%d\n", error);
1394                                                 return (error);
1395                                         }
1396
1397                                         EROM_LOG(sc, "\t\tsizehi:\t0x%x\n",
1398                                             entry);
1399                                 }
1400                         }
1401                         break;
1402                 }
1403
1404                 default:
1405                         EROM_LOG(sc, "unknown EROM entry 0x%x (type=%s)\n",
1406                             entry, bcma_erom_entry_type_name(entry));
1407                         return (EINVAL);
1408                 }
1409         }
1410
1411         if (error == ENOENT)
1412                 EROM_LOG(sc, "BCMA EROM table missing terminating EOF\n");
1413         else if (error)
1414                 EROM_LOG(sc, "EROM read failed: %d\n", error);
1415
1416         return (error);
1417 }
1418
1419 static kobj_method_t bcma_erom_methods[] = {
1420         KOBJMETHOD(bhnd_erom_probe,             bcma_erom_probe),
1421         KOBJMETHOD(bhnd_erom_init,              bcma_erom_init),
1422         KOBJMETHOD(bhnd_erom_fini,              bcma_erom_fini),
1423         KOBJMETHOD(bhnd_erom_get_core_table,    bcma_erom_get_core_table),
1424         KOBJMETHOD(bhnd_erom_free_core_table,   bcma_erom_free_core_table),
1425         KOBJMETHOD(bhnd_erom_lookup_core,       bcma_erom_lookup_core),
1426         KOBJMETHOD(bhnd_erom_lookup_core_addr,  bcma_erom_lookup_core_addr),
1427         KOBJMETHOD(bhnd_erom_dump,              bcma_erom_dump),
1428
1429         KOBJMETHOD_END
1430 };
1431
1432 BHND_EROM_DEFINE_CLASS(bcma_erom, bcma_erom_parser, bcma_erom_methods, sizeof(struct bcma_erom));