Index: head/sys/dev/bhnd/bcma/bcma_erom.c =================================================================== --- head/sys/dev/bhnd/bcma/bcma_erom.c (revision 317379) +++ head/sys/dev/bhnd/bcma/bcma_erom.c (revision 317380) @@ -1,1384 +1,1536 @@ /*- * Copyright (c) 2015 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "bcma_eromreg.h" #include "bcma_eromvar.h" /* * BCMA Enumeration ROM (EROM) Table * * Provides auto-discovery of BCMA cores on Broadcom's HND SoC. * * The EROM core address can be found at BCMA_CC_EROM_ADDR within the * ChipCommon registers. The table itself is comprised of 32-bit * type-tagged entries, organized into an array of variable-length * core descriptor records. * * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF) * marker. */ struct bcma_erom_io; static const char *bcma_erom_entry_type_name (uint8_t entry); static uint32_t bcma_eio_read4(struct bcma_erom_io *io, bus_size_t offset); static int bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry); static int bcma_erom_skip32(struct bcma_erom *erom); static int bcma_erom_skip_core(struct bcma_erom *erom); static int bcma_erom_skip_mport(struct bcma_erom *erom); static int bcma_erom_skip_sport_region(struct bcma_erom *erom); static int bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype); static int bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type, bhnd_port_type *port_type); static int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry); static bus_size_t bcma_erom_tell(struct bcma_erom *erom); static void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset); static void bcma_erom_reset(struct bcma_erom *erom); static int bcma_erom_seek_matching_core(struct bcma_erom *sc, const struct bhnd_core_match *desc, struct bhnd_core_info *core); static int bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core); static int bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport); static int bcma_erom_parse_sport_region(struct bcma_erom *erom, struct bcma_erom_sport_region *region); static void bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx, int core_unit, struct bhnd_core_info *info); /** * BCMA EROM generic I/O context */ struct bcma_erom_io { struct bhnd_resource *res; /**< memory resource, or NULL if initialized with bus space tag and handle */ int rid; /**< memory resource id, or -1 */ bus_space_tag_t bst; /**< bus space tag, if any */ bus_space_handle_t bsh; /**< bus space handle, if any */ bus_size_t start; /**< base read offset */ }; /** * BCMA EROM per-instance state. */ struct bcma_erom { struct bhnd_erom obj; device_t dev; /**< parent device, or NULL if none. */ struct bcma_erom_io io; /**< I/O context */ bus_size_t offset; /**< current read offset */ }; #define EROM_LOG(erom, fmt, ...) do { \ if (erom->dev != NULL) { \ device_printf(erom->dev, "erom[0x%llx]: " fmt, \ (unsigned long long) (erom->offset), ##__VA_ARGS__);\ } else { \ printf("erom[0x%llx]: " fmt, \ (unsigned long long) (erom->offset), ##__VA_ARGS__);\ } \ } while(0) /** Return the type name for an EROM entry */ static const char * bcma_erom_entry_type_name (uint8_t entry) { switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: return "core"; case BCMA_EROM_ENTRY_TYPE_MPORT: return "mport"; case BCMA_EROM_ENTRY_TYPE_REGION: return "region"; default: return "unknown"; } } /** * Read a 32-bit value from an EROM I/O context. * * @param io EROM I/O context. * @param offset Read offset. */ static uint32_t bcma_eio_read4(struct bcma_erom_io *io, bus_size_t offset) { bus_size_t read_off; read_off = io->start + offset; if (io->res != NULL) return (bhnd_bus_read_4(io->res, read_off)); else return (bus_space_read_4(io->bst, io->bsh, read_off)); } /* Initialize bcma_erom resource I/O context */ static void bcma_eio_init(struct bcma_erom_io *io, struct bhnd_resource *res, int rid, bus_size_t offset) { io->res = res; io->rid = rid; io->start = offset; } /* Initialize bcma_erom bus space I/O context */ static void bcma_eio_init_static(struct bcma_erom_io *io, bus_space_tag_t bst, bus_space_handle_t bsh, bus_size_t offset) { io->res = NULL; io->rid = -1; io->bst = bst; io->bsh = bsh; io->start = offset; } /* BCMA implementation of BHND_EROM_INIT() */ static int bcma_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid, device_t parent, int rid) { struct bcma_erom *sc; struct bhnd_resource *res; sc = (struct bcma_erom *)erom; sc->dev = parent; sc->offset = 0; res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid, cid->enum_addr, cid->enum_addr + BCMA_EROM_TABLE_SIZE - 1, BCMA_EROM_TABLE_SIZE, RF_ACTIVE|RF_SHAREABLE); if (res == NULL) return (ENOMEM); bcma_eio_init(&sc->io, res, rid, BCMA_EROM_TABLE_START); return (0); } /* BCMA implementation of BHND_EROM_INIT_STATIC() */ static int bcma_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid, bus_space_tag_t bst, bus_space_handle_t bsh) { struct bcma_erom *sc; sc = (struct bcma_erom *)erom; sc->dev = NULL; sc->offset = 0; bcma_eio_init_static(&sc->io, bst, bsh, BCMA_EROM_TABLE_START); return (0); } /* Common implementation of BHND_EROM_PROBE/BHND_EROM_PROBE_STATIC */ static int bcma_erom_probe_common(struct bcma_erom_io *io, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { uint32_t idreg, eromptr; /* Hints aren't supported; all BCMA devices have a ChipCommon * core */ if (hint != NULL) return (EINVAL); /* Confirm CHIPC_EROMPTR availability */ idreg = bcma_eio_read4(io, CHIPC_ID); if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS))) return (ENXIO); /* Fetch EROM address */ eromptr = bcma_eio_read4(io, CHIPC_EROMPTR); /* Parse chip identifier */ *cid = bhnd_parse_chipid(idreg, eromptr); /* Verify chip type */ switch (cid->chip_type) { case BHND_CHIPTYPE_BCMA: return (BUS_PROBE_DEFAULT); case BHND_CHIPTYPE_BCMA_ALT: case BHND_CHIPTYPE_UBUS: return (BUS_PROBE_GENERIC); default: return (ENXIO); } } static int bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res, bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { struct bcma_erom_io io; bcma_eio_init(&io, res, rman_get_rid(res->res), offset + BCMA_EROM_TABLE_START); return (bcma_erom_probe_common(&io, hint, cid)); } static int bcma_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { struct bcma_erom_io io; bcma_eio_init_static(&io, bst, bsh, BCMA_EROM_TABLE_START); return (bcma_erom_probe_common(&io, hint, cid)); } static void bcma_erom_fini(bhnd_erom_t *erom) { struct bcma_erom *sc = (struct bcma_erom *)erom; if (sc->io.res != NULL) { bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->io.rid, sc->io.res); sc->io.res = NULL; sc->io.rid = -1; } } static int bcma_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct bcma_erom *sc = (struct bcma_erom *)erom; /* Search for the first matching core */ return (bcma_erom_seek_matching_core(sc, desc, core)); } static int bcma_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type port_type, u_int port_num, u_int region_num, struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size) { struct bcma_erom *sc; struct bcma_erom_core ec; uint32_t entry; uint8_t region_port, region_type; bool found; int error; sc = (struct bcma_erom *)erom; /* Seek to the first matching core and provide the core info * to the caller */ if ((error = bcma_erom_seek_matching_core(sc, desc, core))) return (error); if ((error = bcma_erom_parse_core(sc, &ec))) return (error); /* Skip master ports */ for (u_long i = 0; i < ec.num_mport; i++) { if ((error = bcma_erom_skip_mport(sc))) return (error); } /* Seek to the region block for the given port type */ found = false; while (1) { bhnd_port_type p_type; uint8_t r_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Expected region type? */ r_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); error = bcma_erom_region_to_port_type(sc, r_type, &p_type); if (error) return (error); if (p_type == port_type) { found = true; break; } /* Skip to next entry */ if ((error = bcma_erom_skip_sport_region(sc))) return (error); } if (!found) return (ENOENT); /* Found the appropriate port type block; now find the region records * for the given port number */ found = false; for (u_int i = 0; i <= port_num; i++) { bhnd_port_type p_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Fetch the type/port of the first region entry */ region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); /* Have we found the region entries for the desired port? */ if (i == port_num) { error = bcma_erom_region_to_port_type(sc, region_type, &p_type); if (error) return (error); if (p_type == port_type) found = true; break; } /* Otherwise, seek to next block of region records */ while (1) { uint8_t next_type, next_port; if ((error = bcma_erom_skip_sport_region(sc))) return (error); if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); if (next_type != region_type || next_port != region_port) break; } } if (!found) return (ENOENT); /* Finally, search for the requested region number */ for (u_int i = 0; i <= region_num; i++) { struct bcma_erom_sport_region region; uint8_t next_port, next_type; if ((error = bcma_erom_peek32(sc, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, REGION)) return (ENOENT); /* Check for the end of the region block */ next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); if (next_type != region_type || next_port != region_port) break; /* Parse the region */ if ((error = bcma_erom_parse_sport_region(sc, ®ion))) return (error); /* Is this our target region_num? */ if (i == region_num) { /* Found */ *addr = region.base_addr; *size = region.size; return (0); } } /* Not found */ return (ENOENT); }; static int bcma_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { struct bcma_erom *sc; struct bhnd_core_info *buffer; bus_size_t initial_offset; u_int count; int error; sc = (struct bcma_erom *)erom; buffer = NULL; initial_offset = bcma_erom_tell(sc); /* Determine the core count */ bcma_erom_reset(sc); for (count = 0, error = 0; !error; count++) { struct bcma_erom_core core; /* Seek to the first readable core entry */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error == ENOENT) break; else if (error) goto cleanup; /* Read past the core descriptor */ if ((error = bcma_erom_parse_core(sc, &core))) goto cleanup; } /* Allocate our output buffer */ buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND, M_NOWAIT); if (buffer == NULL) { error = ENOMEM; goto cleanup; } /* Parse all core descriptors */ bcma_erom_reset(sc); for (u_int i = 0; i < count; i++) { struct bcma_erom_core core; int unit; /* Parse the core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) goto cleanup; error = bcma_erom_parse_core(sc, &core); if (error) goto cleanup; /* Determine the unit number */ unit = 0; for (u_int j = 0; j < i; j++) { if (buffer[i].vendor == buffer[j].vendor && buffer[i].device == buffer[j].device) unit++; } /* Convert to a bhnd info record */ bcma_erom_to_core_info(&core, i, unit, &buffer[i]); } cleanup: if (!error) { *cores = buffer; *num_cores = count; } else { if (buffer != NULL) free(buffer, M_BHND); } /* Restore the initial position */ bcma_erom_seek(sc, initial_offset); return (error); } static void bcma_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { free(cores, M_BHND); } /** * Return the current read position. */ static bus_size_t bcma_erom_tell(struct bcma_erom *erom) { return (erom->offset); } /** * Seek to an absolute read position. */ static void bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset) { erom->offset = offset; } /** * Read a 32-bit entry value from the EROM table without advancing the * read position. * * @param erom EROM read state. * @param entry Will contain the read result on success. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry) { if (erom->offset >= (BCMA_EROM_TABLE_SIZE - sizeof(uint32_t))) { EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n"); return (EINVAL); } *entry = bcma_eio_read4(&erom->io, erom->offset); return (0); } /** * Read a 32-bit entry value from the EROM table. * * @param erom EROM read state. * @param entry Will contain the read result on success. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry) { int error; if ((error = bcma_erom_peek32(erom, entry)) == 0) erom->offset += 4; return (error); } /** * Read and discard 32-bit entry value from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip32(struct bcma_erom *erom) { uint32_t entry; return bcma_erom_read32(erom, &entry); } /** * Read and discard a core descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_core(struct bcma_erom *erom) { struct bcma_erom_core core; return (bcma_erom_parse_core(erom, &core)); } /** * Read and discard a master port descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_mport(struct bcma_erom *erom) { struct bcma_erom_mport mp; return (bcma_erom_parse_mport(erom, &mp)); } /** * Read and discard a port region descriptor from the EROM table. * * @param erom EROM read state. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero The read could not be completed. */ static int bcma_erom_skip_sport_region(struct bcma_erom *erom) { struct bcma_erom_sport_region r; return (bcma_erom_parse_sport_region(erom, &r)); } /** * Seek to the next entry matching the given EROM entry type. * * @param erom EROM read state. * @param etype One of BCMA_EROM_ENTRY_TYPE_CORE, * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype) { uint32_t entry; int error; /* Iterate until we hit an entry matching the requested type. */ while (!(error = bcma_erom_peek32(erom, &entry))) { /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) return (ENOENT); /* Invalid entry */ if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) return (EINVAL); /* Entry type matches? */ if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype) return (0); /* Skip non-matching entry types. */ switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { case BCMA_EROM_ENTRY_TYPE_CORE: if ((error = bcma_erom_skip_core(erom))) return (error); break; case BCMA_EROM_ENTRY_TYPE_MPORT: if ((error = bcma_erom_skip_mport(erom))) return (error); break; case BCMA_EROM_ENTRY_TYPE_REGION: if ((error = bcma_erom_skip_sport_region(erom))) return (error); break; default: /* Unknown entry type! */ return (EINVAL); } } return (error); } /** * Return the read position to the start of the EROM table. * * @param erom EROM read state. */ static void bcma_erom_reset(struct bcma_erom *erom) { erom->offset = 0; } /** * Seek to the first core entry matching @p desc. * * @param erom EROM read state. * @param desc The core match descriptor. * @param[out] core On success, the matching core info. If the core info * is not desired, a NULL pointer may be provided. * @retval 0 success * @retval ENOENT The end of the EROM table was reached before @p index was * found. * @retval non-zero Reading or parsing failed. */ static int bcma_erom_seek_matching_core(struct bcma_erom *sc, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct bhnd_core_match imatch; bus_size_t core_offset, next_offset; int error; /* Seek to table start. */ bcma_erom_reset(sc); /* We can't determine a core's unit number during the initial scan. */ imatch = *desc; imatch.m.match.core_unit = 0; /* Locate the first matching core */ for (u_int i = 0; i < UINT_MAX; i++) { struct bcma_erom_core ec; struct bhnd_core_info ci; /* Seek to the next core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); /* Save the core offset */ core_offset = bcma_erom_tell(sc); /* Parse the core */ if ((error = bcma_erom_parse_core(sc, &ec))) return (error); bcma_erom_to_core_info(&ec, i, 0, &ci); /* Check for initial match */ if (!bhnd_core_matches(&ci, &imatch)) continue; /* Re-scan preceding cores to determine the unit number. */ next_offset = bcma_erom_tell(sc); bcma_erom_reset(sc); for (u_int j = 0; j < i; j++) { /* Parse the core */ error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); if ((error = bcma_erom_parse_core(sc, &ec))) return (error); /* Bump the unit number? */ if (ec.vendor == ci.vendor && ec.device == ci.device) ci.unit++; } /* Check for full match against now-valid unit number */ if (!bhnd_core_matches(&ci, desc)) { /* Reposition to allow reading the next core */ bcma_erom_seek(sc, next_offset); continue; } /* Found; seek to the core's initial offset and provide * the core info to the caller */ bcma_erom_seek(sc, core_offset); if (core != NULL) *core = ci; return (0); } /* Not found, or a parse error occured */ return (error); } /** * Read the next core descriptor from the EROM table. * * @param erom EROM read state. * @param[out] core On success, will be populated with the parsed core * descriptor data. * @retval 0 success * @retval ENOENT The end of the EROM table was reached. * @retval non-zero Reading or parsing the core descriptor failed. */ static int bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core) { uint32_t entry; int error; /* Parse CoreDescA */ if ((error = bcma_erom_read32(erom, &entry))) return (error); /* Handle EOF */ if (entry == BCMA_EROM_TABLE_EOF) return (ENOENT); if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n", entry, bcma_erom_entry_type_name(entry)); return (EINVAL); } core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER); core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID); /* Parse CoreDescB */ if ((error = bcma_erom_read32(erom, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { return (EINVAL); } core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV); core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP); core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP); core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP); core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP); return (0); } /** * Read the next master port descriptor from the EROM table. * * @param erom EROM read state. * @param[out] mport On success, will be populated with the parsed * descriptor data. * @retval 0 success * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport) { uint32_t entry; int error; /* Parse the master port descriptor */ if ((error = bcma_erom_read32(erom, &entry))) return (error); if (!BCMA_EROM_ENTRY_IS(entry, MPORT)) return (EINVAL); mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID); mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM); return (0); } /** * Read the next slave port region descriptor from the EROM table. * * @param erom EROM read state. * @param[out] mport On success, will be populated with the parsed * descriptor data. * @retval 0 success * @retval ENOENT The end of the region descriptor table was reached. * @retval non-zero Reading or parsing the descriptor failed. */ static int bcma_erom_parse_sport_region(struct bcma_erom *erom, struct bcma_erom_sport_region *region) { uint32_t entry; uint8_t size_type; int error; /* Peek at the region descriptor */ if (bcma_erom_peek32(erom, &entry)) return (EINVAL); /* A non-region entry signals the end of the region table */ if (!BCMA_EROM_ENTRY_IS(entry, REGION)) { return (ENOENT); } else { bcma_erom_skip32(erom); } region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE); region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE); /* If region address is 64-bit, fetch the high bits. */ if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->base_addr |= ((bhnd_addr_t) entry << 32); } /* Parse the region size; it's either encoded as the binary logarithm * of the number of 4K pages (i.e. log2 n), or its encoded as a * 32-bit/64-bit literal value directly following the current entry. */ if (size_type == BCMA_EROM_REGION_SIZE_OTHER) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL); if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) { if ((error = bcma_erom_read32(erom, &entry))) return (error); region->size |= ((bhnd_size_t) entry << 32); } } else { region->size = BCMA_EROM_REGION_SIZE_BASE << size_type; } /* Verify that addr+size does not overflow. */ if (region->size != 0 && BHND_ADDR_MAX - (region->size - 1) < region->base_addr) { EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n", bcma_erom_entry_type_name(region->region_type), region->region_port, (unsigned long long) region->base_addr, (unsigned long long) region->size); return (EINVAL); } return (0); } /** * Convert a bcma_erom_core record to its bhnd_core_info representation. * * @param core EROM core record to convert. * @param core_idx The core index of @p core. * @param core_unit The core unit of @p core. * @param[out] info The populated bhnd_core_info representation. */ static void bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx, int core_unit, struct bhnd_core_info *info) { info->vendor = core->vendor; info->device = core->device; info->hwrev = core->rev; info->core_idx = core_idx; info->unit = core_unit; } /** * Map an EROM region type to its corresponding port type. * * @param region_type Region type value. * @param[out] port_type On success, the corresponding port type. */ static int bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type, bhnd_port_type *port_type) { switch (region_type) { case BCMA_EROM_REGION_TYPE_DEVICE: *port_type = BHND_PORT_DEVICE; return (0); case BCMA_EROM_REGION_TYPE_BRIDGE: *port_type = BHND_PORT_BRIDGE; return (0); case BCMA_EROM_REGION_TYPE_MWRAP: case BCMA_EROM_REGION_TYPE_SWRAP: *port_type = BHND_PORT_AGENT; return (0); default: EROM_LOG(erom, "unsupported region type %hhx\n", region_type); return (EINVAL); } } /** * Register all MMIO region descriptors for the given slave port. * * @param erom EROM read state. * @param corecfg Core info to be populated with the scanned port regions. * @param port_num Port index for which regions will be parsed. * @param region_type The region type to be parsed. * @param[out] offset The offset at which to perform parsing. On success, this * will be updated to point to the next EROM table entry. */ static int bcma_erom_corecfg_fill_port_regions(struct bcma_erom *erom, struct bcma_corecfg *corecfg, bcma_pid_t port_num, uint8_t region_type) { struct bcma_sport *sport; struct bcma_sport_list *sports; bus_size_t entry_offset; int error; bhnd_port_type port_type; error = 0; /* Determine the port type for this region type. */ error = bcma_erom_region_to_port_type(erom, region_type, &port_type); if (error) return (error); /* Fetch the list to be populated */ sports = bcma_corecfg_get_port_list(corecfg, port_type); /* Allocate a new port descriptor */ sport = bcma_alloc_sport(port_num, port_type); if (sport == NULL) return (ENOMEM); /* Read all address regions defined for this port */ for (bcma_rmid_t region_num = 0;; region_num++) { struct bcma_map *map; struct bcma_erom_sport_region spr; /* No valid port definition should come anywhere near * BCMA_RMID_MAX. */ if (region_num == BCMA_RMID_MAX) { EROM_LOG(erom, "core%u %s%u: region count reached " "upper limit of %u\n", corecfg->core_info.core_idx, bhnd_port_type_name(port_type), port_num, BCMA_RMID_MAX); error = EINVAL; goto cleanup; } /* Parse the next region entry. */ entry_offset = bcma_erom_tell(erom); error = bcma_erom_parse_sport_region(erom, &spr); if (error && error != ENOENT) { EROM_LOG(erom, "core%u %s%u.%u: invalid slave port " "address region\n", corecfg->core_info.core_idx, bhnd_port_type_name(port_type), port_num, region_num); goto cleanup; } /* ENOENT signals no further region entries */ if (error == ENOENT) { /* No further entries */ error = 0; break; } /* A region or type mismatch also signals no further region * entries */ if (spr.region_port != port_num || spr.region_type != region_type) { /* We don't want to consume this entry */ bcma_erom_seek(erom, entry_offset); error = 0; goto cleanup; } /* * Create the map entry. */ map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT); if (map == NULL) { error = ENOMEM; goto cleanup; } map->m_region_num = region_num; map->m_base = spr.base_addr; map->m_size = spr.size; map->m_rid = -1; /* Add the region map to the port */ STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link); sport->sp_num_maps++; } cleanup: /* Append the new port descriptor on success, or deallocate the * partially parsed descriptor on failure. */ if (error == 0) { STAILQ_INSERT_TAIL(sports, sport, sp_link); } else if (sport != NULL) { bcma_free_sport(sport); } return error; } /** * Parse the next core entry from the EROM table and produce a bcma_corecfg * to be owned by the caller. * * @param erom A bcma EROM instance. * @param[out] result On success, the core's device info. The caller inherits * ownership of this allocation. * * @return If successful, returns 0. If the end of the EROM table is hit, * ENOENT will be returned. On error, returns a non-zero error value. */ int bcma_erom_next_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result) { struct bcma_corecfg *cfg; struct bcma_erom_core core; uint8_t first_region_type; bus_size_t initial_offset; u_int core_index; int core_unit; int error; cfg = NULL; initial_offset = bcma_erom_tell(erom); /* Parse the next core entry */ if ((error = bcma_erom_parse_core(erom, &core))) return (error); /* Determine the core's index and unit numbers */ bcma_erom_reset(erom); core_unit = 0; core_index = 0; for (; bcma_erom_tell(erom) != initial_offset; core_index++) { struct bcma_erom_core prev_core; /* Parse next core */ error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); if ((error = bcma_erom_parse_core(erom, &prev_core))) return (error); /* Is earlier unit? */ if (core.vendor == prev_core.vendor && core.device == prev_core.device) { core_unit++; } /* Seek to next core */ error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); if (error) return (error); } /* We already parsed the core descriptor */ if ((error = bcma_erom_skip_core(erom))) return (error); /* Allocate our corecfg */ cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor, core.device, core.rev); if (cfg == NULL) return (ENOMEM); /* These are 5-bit values in the EROM table, and should never be able * to overflow BCMA_PID_MAX. */ KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count")); KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count")); KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX, ("unsupported wport count")); if (bootverbose) { EROM_LOG(erom, "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n", core_index, bhnd_vendor_name(core.vendor), bhnd_find_core_name(core.vendor, core.device), core.device, core.rev, core_unit); } cfg->num_master_ports = core.num_mport; cfg->num_dev_ports = 0; /* determined below */ cfg->num_bridge_ports = 0; /* determined blow */ cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap; /* Parse Master Port Descriptors */ for (uint8_t i = 0; i < core.num_mport; i++) { struct bcma_mport *mport; struct bcma_erom_mport mpd; /* Parse the master port descriptor */ error = bcma_erom_parse_mport(erom, &mpd); if (error) goto failed; /* Initialize a new bus mport structure */ mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT); if (mport == NULL) { error = ENOMEM; goto failed; } mport->mp_vid = mpd.port_vid; mport->mp_num = mpd.port_num; /* Update dinfo */ STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link); } /* * Determine whether this is a bridge device; if so, we can * expect the first sequence of address region descriptors to * be of EROM_REGION_TYPE_BRIDGE instead of * BCMA_EROM_REGION_TYPE_DEVICE. * * It's unclear whether this is the correct mechanism by which we * should detect/handle bridge devices, but this approach matches * that of (some of) Broadcom's published drivers. */ if (core.num_dport > 0) { uint32_t entry; if ((error = bcma_erom_peek32(erom, &entry))) goto failed; if (BCMA_EROM_ENTRY_IS(entry, REGION) && BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE) { first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE; cfg->num_dev_ports = 0; cfg->num_bridge_ports = core.num_dport; } else { first_region_type = BCMA_EROM_REGION_TYPE_DEVICE; cfg->num_dev_ports = core.num_dport; cfg->num_bridge_ports = 0; } } /* Device/bridge port descriptors */ for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) { error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, first_region_type); if (error) goto failed; } /* Wrapper (aka device management) descriptors (for master ports). */ for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) { error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, BCMA_EROM_REGION_TYPE_MWRAP); if (error) goto failed; } /* Wrapper (aka device management) descriptors (for slave ports). */ for (uint8_t i = 0; i < core.num_swrap; i++) { /* Slave wrapper ports are not numbered distinctly from master * wrapper ports. */ /* * Broadcom DDR1/DDR2 Memory Controller * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) -> * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2) * * ARM BP135 AMBA3 AXI to APB Bridge * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) -> * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2) * * core.num_mwrap * ===> * (core.num_mwrap > 0) ? * core.num_mwrap : * ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) */ uint8_t sp_num; sp_num = (core.num_mwrap > 0) ? core.num_mwrap : ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i; error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num, BCMA_EROM_REGION_TYPE_SWRAP); if (error) goto failed; } /* * Seek to the next core entry (if any), skipping any dangling/invalid * region entries. * * On the BCM4706, the EROM entry for the memory controller core * (0x4bf/0x52E) contains a dangling/unused slave wrapper port region * descriptor. */ if ((error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE))) { if (error != ENOENT) goto failed; } *result = cfg; return (0); failed: if (cfg != NULL) bcma_free_corecfg(cfg); return error; } +static int +bcma_erom_dump(bhnd_erom_t *erom) +{ + struct bcma_erom *sc; + uint32_t entry; + int error; + + sc = (struct bcma_erom *)erom; + + bcma_erom_reset(sc); + + while (!(error = bcma_erom_read32(sc, &entry))) { + /* Handle EOF */ + if (entry == BCMA_EROM_TABLE_EOF) { + EROM_LOG(sc, "EOF\n"); + return (0); + } + + /* Invalid entry */ + if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) { + EROM_LOG(sc, "invalid EROM entry %#x\n", entry); + return (EINVAL); + } + + switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { + case BCMA_EROM_ENTRY_TYPE_CORE: { + /* CoreDescA */ + EROM_LOG(sc, "coreA (0x%x)\n", entry); + EROM_LOG(sc, "\tdesigner:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER)); + EROM_LOG(sc, "\tid:\t\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREA_ID)); + EROM_LOG(sc, "\tclass:\t\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREA_CLASS)); + + /* CoreDescB */ + if ((error = bcma_erom_read32(sc, &entry))) { + EROM_LOG(sc, "error reading CoreDescB: %d\n", + error); + return (error); + } + + if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { + EROM_LOG(sc, "invalid core descriptor; found " + "unexpected entry %#x (type=%s)\n", + entry, bcma_erom_entry_type_name(entry)); + return (EINVAL); + } + + EROM_LOG(sc, "coreB (0x%x)\n", entry); + EROM_LOG(sc, "\trev:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREB_REV)); + EROM_LOG(sc, "\tnummp:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP)); + EROM_LOG(sc, "\tnumdp:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP)); + EROM_LOG(sc, "\tnumwmp:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP)); + EROM_LOG(sc, "\tnumwsp:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP)); + + break; + } + case BCMA_EROM_ENTRY_TYPE_MPORT: + EROM_LOG(sc, "\tmport 0x%x\n", entry); + EROM_LOG(sc, "\t\tport:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, MPORT_NUM)); + EROM_LOG(sc, "\t\tid:\t\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, MPORT_ID)); + break; + + case BCMA_EROM_ENTRY_TYPE_REGION: { + bool addr64; + uint8_t size_type; + + addr64 = (BCMA_EROM_GET_ATTR(entry, REGION_64BIT) != 0); + size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE); + + EROM_LOG(sc, "\tregion 0x%x:\n", entry); + EROM_LOG(sc, "\t\t%s:\t0x%x\n", + addr64 ? "baselo" : "base", + BCMA_EROM_GET_ATTR(entry, REGION_BASE)); + EROM_LOG(sc, "\t\tport:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, REGION_PORT)); + EROM_LOG(sc, "\t\ttype:\t0x%x\n", + BCMA_EROM_GET_ATTR(entry, REGION_TYPE)); + EROM_LOG(sc, "\t\tsztype:\t0x%hhx\n", size_type); + + /* Read the base address high bits */ + if (addr64) { + if ((error = bcma_erom_read32(sc, &entry))) { + EROM_LOG(sc, "error reading region " + "base address high bits %d\n", + error); + return (error); + } + + EROM_LOG(sc, "\t\tbasehi:\t0x%x\n", entry); + } + + /* Read extended size descriptor */ + if (size_type == BCMA_EROM_REGION_SIZE_OTHER) { + bool size64; + + if ((error = bcma_erom_read32(sc, &entry))) { + EROM_LOG(sc, "error reading region " + "size descriptor %d\n", + error); + return (error); + } + + if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) + size64 = true; + else + size64 = false; + + EROM_LOG(sc, "\t\t%s:\t0x%x\n", + size64 ? "sizelo" : "size", + BCMA_EROM_GET_ATTR(entry, RSIZE_VAL)); + + if (size64) { + error = bcma_erom_read32(sc, &entry); + if (error) { + EROM_LOG(sc, "error reading " + "region size high bits: " + "%d\n", error); + return (error); + } + + EROM_LOG(sc, "\t\tsizehi:\t0x%x\n", + entry); + } + } + break; + } + + default: + EROM_LOG(sc, "unknown EROM entry 0x%x (type=%s)\n", + entry, bcma_erom_entry_type_name(entry)); + return (EINVAL); + } + } + + if (error == ENOENT) + EROM_LOG(sc, "BCMA EROM table missing terminating EOF\n"); + else if (error) + EROM_LOG(sc, "EROM read failed: %d\n", error); + + return (error); +} + static kobj_method_t bcma_erom_methods[] = { KOBJMETHOD(bhnd_erom_probe, bcma_erom_probe), KOBJMETHOD(bhnd_erom_probe_static, bcma_erom_probe_static), KOBJMETHOD(bhnd_erom_init, bcma_erom_init), KOBJMETHOD(bhnd_erom_init_static, bcma_erom_init_static), KOBJMETHOD(bhnd_erom_fini, bcma_erom_fini), KOBJMETHOD(bhnd_erom_get_core_table, bcma_erom_get_core_table), KOBJMETHOD(bhnd_erom_free_core_table, bcma_erom_free_core_table), KOBJMETHOD(bhnd_erom_lookup_core, bcma_erom_lookup_core), KOBJMETHOD(bhnd_erom_lookup_core_addr, bcma_erom_lookup_core_addr), + KOBJMETHOD(bhnd_erom_dump, bcma_erom_dump), KOBJMETHOD_END }; BHND_EROM_DEFINE_CLASS(bcma_erom, bcma_erom_parser, bcma_erom_methods, sizeof(struct bcma_erom)); Index: head/sys/dev/bhnd/bhnd_erom.h =================================================================== --- head/sys/dev/bhnd/bhnd_erom.h (revision 317379) +++ head/sys/dev/bhnd/bhnd_erom.h (revision 317380) @@ -1,243 +1,258 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_EROM_BHND_EROM_H_ #define _BHND_EROM_BHND_EROM_H_ #include #include #include #include #include #include "bhnd_erom_if.h" bhnd_erom_t *bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid, device_t parent, int rid); int bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize, const struct bhnd_chipid *cid, bus_space_tag_t bst, bus_space_handle_t bsh); void bhnd_erom_fini_static(bhnd_erom_t *erom); void bhnd_erom_free(bhnd_erom_t *erom); /** * Abstract bhnd_erom instance state. Must be first member of all subclass * instances. */ struct bhnd_erom { KOBJ_FIELDS; }; /** Number of additional bytes to reserve for statically allocated * bhnd_erom instances. */ #define BHND_EROM_STATIC_BYTES 64 /** * A bhnd_erom instance structure large enough to statically allocate * any known bhnd_erom subclass. * * The maximum size of subclasses is verified statically in * BHND_EROM_DEFINE_CLASS(), and at runtime in bhnd_erom_init_static(). */ struct bhnd_erom_static { struct bhnd_erom obj; uint8_t idata[BHND_EROM_STATIC_BYTES]; }; /** Registered EROM parser class instances. */ SET_DECLARE(bhnd_erom_class_set, bhnd_erom_class_t); #define BHND_EROM_DEFINE_CLASS(name, classvar, methods, size) \ DEFINE_CLASS_0(name, classvar, methods, size); \ BHND_EROM_CLASS_DEF(classvar); \ _Static_assert(size <= sizeof(struct bhnd_erom_static), \ "cannot statically allocate instance data; " \ "increase BHND_EROM_STATIC_BYTES"); #define BHND_EROM_CLASS_DEF(classvar) DATA_SET(bhnd_erom_class_set, classvar) /** * Probe to see if this device enumeration class supports the bhnd bus * mapped by the given resource, returning a standard newbus device probe * result (see BUS_PROBE_*) and the probed chip identification. * * @param cls The erom class to probe. * @param res A resource mapping the first bus core (EXTIF or * ChipCommon) * @param offset Offset to the first bus core within @p res. * @param hint Identification hint used to identify the device. If * chipset supports standard chip identification registers * within the first core, this parameter should be NULL. * @param[out] cid On success, the probed chip identifier. * * @retval 0 if this is the only possible device enumeration * parser for the probed bus. * @retval negative if the probe succeeds, a negative value should be * returned; the parser returning the highest negative * value will be selected to handle device enumeration. * @retval ENXIO If the bhnd bus type is not handled by this parser. * @retval positive if an error occurs during probing, a regular unix error * code should be returned. */ static inline int bhnd_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res, bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { return (BHND_EROM_PROBE(cls, res, offset, hint, cid)); } /** * Probe to see if this device enumeration class supports the bhnd bus * mapped at the given bus space tag and handle, returning a standard * newbus device probe result (see BUS_PROBE_*) and the probed * chip identification. * * @param cls The erom class to probe. * @param bst Bus space tag. * @param bsh Bus space handle mapping the EXTIF or ChipCommon core. * @param paddr The physical address of the core mapped by @p bst and * @p bsh. * @param hint Identification hint used to identify the device. If * chipset supports standard chip identification registers * within the first core, this parameter should be NULL. * @param[out] cid On success, the probed chip identifier. * * @retval 0 if this is the only possible device enumeration * parser for the probed bus. * @retval negative if the probe succeeds, a negative value should be * returned; the parser returning the lowest value will * be selected to handle device enumeration. * @retval ENXIO If the bhnd bus type is not handled by this parser. * @retval positive if an error occurs during probing, a regular unix error * code should be returned. */ static inline int bhnd_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { return (BHND_EROM_PROBE_STATIC(cls, bst, bsh, paddr, hint, cid)); } /** * Parse all cores descriptors in @p erom, returning the array in @p cores and * the count in @p num_cores. * * The memory allocated for the table must be freed via * bhnd_erom_free_core_table(). * * @param erom The erom parser to be queried. * @param[out] cores The table of parsed core descriptors. * @param[out] num_cores The number of core records in @p cores. * * @retval 0 success * @retval non-zero if an error occurs, a regular unix error code will * be returned. */ static inline int bhnd_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { return (BHND_EROM_GET_CORE_TABLE(erom, cores, num_cores)); } /** * Free any memory allocated in a previous call to BHND_EROM_GET_CORE_TABLE(). * * @param erom The erom parser instance. * @param cores A core table allocated by @p erom. */ static inline void bhnd_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { return (BHND_EROM_FREE_CORE_TABLE(erom, cores)); }; /** * Locate the first core table entry in @p erom that matches @p desc. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param[out] core On success, the matching core info record. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval non-zero Reading or parsing failed. */ static inline int bhnd_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { return (BHND_EROM_LOOKUP_CORE(erom, desc, core)); } /** * Locate the first core table entry in @p erom that matches @p desc, * and return the specified port region's base address and size. * * If a core matching @p desc is not found, or the requested port region * is not mapped to the matching core, ENOENT is returned. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param type The port type to search for. * @param port The port to search for. * @param region The port region to search for. * @param[out] core If not NULL, will be populated with the matched core * info record on success. * @param[out] addr On success, the base address of the port region. * @param[out] size On success, the total size of the port region. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval ENOENT No port region matching @p type, @p port, and @p region * was found. * @retval non-zero Reading or parsing failed. */ static inline int bhnd_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size) { return (BHND_EROM_LOOKUP_CORE_ADDR(erom, desc, type, port, region, core, addr, size)); }; +/** + * Enumerate and print all entries in @p erom. + * + * @param erom The erom parser to be enumerated. + * + * @retval 0 success + * @retval non-zero If an error occurs parsing the EROM table, a regular + * unix error code will be returned. + */ +static inline int +bhnd_erom_dump(bhnd_erom_t *erom) +{ + return (BHND_EROM_DUMP(erom)); +} + #endif /* _BHND_EROM_BHND_EROM_H_ */ Index: head/sys/dev/bhnd/bhnd_erom_if.m =================================================================== --- head/sys/dev/bhnd/bhnd_erom_if.m (revision 317379) +++ head/sys/dev/bhnd/bhnd_erom_if.m (revision 317380) @@ -1,243 +1,256 @@ #- # Copyright (c) 2016 Landon Fuller # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ #include #include #include #include #include #include #include INTERFACE bhnd_erom; # # bhnd(4) device enumeration. # # Provides a common parser interface to the incompatible device enumeration # tables used by bhnd(4) buses. # /** * Probe to see if this device enumeration class supports the bhnd bus * mapped by the given resource, returning a standard newbus device probe * result (see BUS_PROBE_*) and the probed chip identification. * * @param cls The erom class to probe. * @param res A resource mapping the first bus core. * @param offset Offset to the first bus core within @p res. * @param hint Hint used to identify the device. If chipset supports * standard chip identification registers within the first * core, this parameter should be NULL. * @param[out] cid On success, the probed chip identifier. * * @retval 0 if this is the only possible device enumeration * parser for the probed bus. * @retval negative if the probe succeeds, a negative value should be * returned; the parser returning the highest negative * value will be selected to handle device enumeration. * @retval ENXIO If the bhnd bus type is not handled by this parser. * @retval positive if an error occurs during probing, a regular unix error * code should be returned. */ STATICMETHOD int probe { bhnd_erom_class_t *cls; struct bhnd_resource *res; bus_size_t offset; const struct bhnd_chipid *hint; struct bhnd_chipid *cid; }; /** * Probe to see if this device enumeration class supports the bhnd bus * mapped at the given bus space tag and handle, returning a standard * newbus device probe result (see BUS_PROBE_*) and the probed * chip identification. * * @param cls The erom class to probe. * @param bst Bus space tag. * @param bsh Bus space handle mapping the first bus core. * @param paddr The physical address of the core mapped by @p bst and * @p bsh. * @param hint Hint used to identify the device. If chipset supports * standard chip identification registers within the first * core, this parameter should be NULL. * @param[out] cid On success, the probed chip identifier. * * @retval 0 if this is the only possible device enumeration * parser for the probed bus. * @retval negative if the probe succeeds, a negative value should be * returned; the parser returning the highest negative * value will be selected to handle device enumeration. * @retval ENXIO If the bhnd bus type is not handled by this parser. * @retval positive if an error occurs during probing, a regular unix error * code should be returned. */ STATICMETHOD int probe_static { bhnd_erom_class_t *cls; bus_space_tag_t bst; bus_space_handle_t bsh; bus_addr_t paddr; const struct bhnd_chipid *hint; struct bhnd_chipid *cid; }; /** * Initialize a device enumeration table parser. * * @param erom The erom parser to initialize. * @param cid The device's chip identifier. * @param parent The parent device from which EROM resources should * be allocated. * @param rid The resource id to be used when allocating the * enumeration table. * * @retval 0 success * @retval non-zero if an error occurs initializing the EROM parser, * a regular unix error code will be returned. */ METHOD int init { bhnd_erom_t *erom; const struct bhnd_chipid *cid; device_t parent; int rid; }; /** * Initialize an device enumeration table parser using the provided bus space * tag and handle. * * @param erom The erom parser to initialize. * @param cid The device's chip identifier. * @param bst Bus space tag. * @param bsh Bus space handle mapping the full bus enumeration * space. * * @retval 0 success * @retval non-zero if an error occurs initializing the EROM parser, * a regular unix error code will be returned. */ METHOD int init_static { bhnd_erom_t *erom; const struct bhnd_chipid *cid; bus_space_tag_t bst; bus_space_handle_t bsh; }; /** * Release all resources held by @p erom. * * @param erom An erom parser instance previously initialized via * BHND_EROM_INIT() or BHND_EROM_INIT_STATIC(). */ METHOD void fini { bhnd_erom_t *erom; }; /** * Parse all cores descriptors, returning the array in @p cores and the count * in @p num_cores. * * The memory allocated for the table must be freed via * BHND_EROM_FREE_CORE_TABLE(). * * @param erom The erom parser to be queried. * @param[out] cores The table of parsed core descriptors. * @param[out] num_cores The number of core records in @p cores. * * @retval 0 success * @retval non-zero if an error occurs, a regular unix error code will * be returned. */ METHOD int get_core_table { bhnd_erom_t *erom; struct bhnd_core_info **cores; u_int *num_cores; }; /** * Free any memory allocated in a previous call to BHND_EROM_GET_CORE_TABLE(). * * @param erom The erom parser instance. * @param cores A core table allocated by @p erom. */ METHOD void free_core_table { bhnd_erom_t *erom; struct bhnd_core_info *cores; }; /** * Locate the first core table entry in @p erom that matches @p desc. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param[out] core On success, the matching core info record. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval non-zero Reading or parsing failed. */ METHOD int lookup_core { bhnd_erom_t *erom; const struct bhnd_core_match *desc; struct bhnd_core_info *core; }; /** * Locate the first core table entry in @p erom that matches @p desc, * and return the specified port region's base address and size. * * If a core matching @p desc is not found, or the requested port region * is not mapped to the matching core, ENOENT is returned. * * @param erom The erom parser to be queried. * @param desc A core match descriptor. * @param type The port type to search for. * @param port The port to search for. * @param region The port region to search for. * @param[out] core If not NULL, will be populated with the matched core * info record on success. * @param[out] addr On success, the base address of the port region. * @param[out] size On success, the total size of the port region. * * @retval 0 success * @retval ENOENT No core matching @p desc was found. * @retval ENOENT No port region matching @p type, @p port, and @p region * was found. * @retval non-zero Reading or parsing failed. */ METHOD int lookup_core_addr { bhnd_erom_t *erom; const struct bhnd_core_match *desc; bhnd_port_type type; u_int port; u_int region; struct bhnd_core_info *core; bhnd_addr_t *addr; bhnd_size_t *size; }; + +/** + * Enumerate and print all EROM table entries. + * + * @param erom The erom parser to be enumerated. + * + * @retval 0 success + * @retval non-zero If an error occurs reading the EROM table, a regular + * unix error code will be returned. + */ +METHOD int dump { + bhnd_erom_t *erom; +}; Index: head/sys/dev/bhnd/siba/siba_erom.c =================================================================== --- head/sys/dev/bhnd/siba/siba_erom.c (revision 317379) +++ head/sys/dev/bhnd/siba/siba_erom.c (revision 317380) @@ -1,536 +1,596 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "sibareg.h" #include "sibavar.h" struct siba_erom; struct siba_erom_io; static int siba_eio_init(struct siba_erom_io *io, device_t parent, struct bhnd_resource *res, int rid, bus_size_t offset, u_int ncores); static int siba_eio_init_static(struct siba_erom_io *io, bus_space_tag_t bst, bus_space_handle_t bsh, bus_size_t offset, u_int ncores); static uint32_t siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset); static struct siba_core_id siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit); static int siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr, struct bhnd_chipid *cid); /** * SIBA EROM generic I/O context */ struct siba_erom_io { u_int ncores; /**< core count */ bus_size_t offset; /**< base read offset */ /* resource state */ device_t dev; /**< parent dev to use for resource allocations, or NULL if unavailable. */ struct bhnd_resource *res; /**< memory resource, or NULL */ int rid; /**< memory resource ID */ /* bus tag state */ bus_space_tag_t bst; /**< bus space tag */ bus_space_handle_t bsh; /**< bus space handle */ }; /** * SIBA EROM per-instance state. */ struct siba_erom { struct bhnd_erom obj; struct siba_erom_io io; /**< i/o context */ }; #define EROM_LOG(io, fmt, ...) do { \ if (io->dev != NULL) { \ device_printf(io->dev, "%s: " fmt, __FUNCTION__, \ ##__VA_ARGS__); \ } else { \ printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__); \ } \ } while(0) static int siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { uint32_t idreg; int error; /* Try using the provided hint. */ if (hint != NULL) { struct siba_core_id sid; /* Validate bus type */ if (hint->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); /* * Verify the first core's IDHIGH/IDLOW identification. * * The core must be a Broadcom core, but must *not* be * a chipcommon core; those shouldn't be hinted. * * The first core on EXTIF-equipped devices varies, but on the * BCM4710, it's a SDRAM core (0x803). */ sid = siba_eio_read_core_id(io, 0, 0); if (sid.core_info.vendor != BHND_MFGID_BCM) return (ENXIO); if (sid.core_info.device == BHND_COREID_CC) return (EINVAL); *cid = *hint; } else { /* Validate bus type */ idreg = siba_eio_read_4(io, 0, CHIPC_ID); if (CHIPC_GET_BITS(idreg, CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA) return (ENXIO); /* Identify the chipset */ if ((error = siba_eio_read_chipid(io, SIBA_ENUM_ADDR, cid))) return (error); /* Verify the chip type */ if (cid->chip_type != BHND_CHIPTYPE_SIBA) return (ENXIO); } /* * gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES * without triggering build failure due to -Wtype-limits * * if (cid.ncores > SIBA_MAX_CORES) * return (EINVAL) */ _Static_assert((2^sizeof(cid->ncores)) <= SIBA_MAX_CORES, "ncores could result in over-read of backing resource"); return (0); } /* SIBA implementation of BHND_EROM_PROBE() */ static int siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res, bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { struct siba_erom_io io; int error, rid; rid = rman_get_rid(res->res); /* Initialize I/O context, assuming at least 1 core exists. */ if ((error = siba_eio_init(&io, NULL, res, rid, offset, 1))) return (error); return (siba_erom_probe_common(&io, hint, cid)); } /* SIBA implementation of BHND_EROM_PROBE_STATIC() */ static int siba_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint, struct bhnd_chipid *cid) { struct siba_erom_io io; int error; /* Initialize I/O context, assuming at least 1 core exists. */ if ((error = siba_eio_init_static(&io, bst, bsh, 0, 1))) return (error); return (siba_erom_probe_common(&io, hint, cid)); } /* SIBA implementation of BHND_EROM_INIT() */ static int siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid, device_t parent, int rid) { struct siba_erom *sc; struct bhnd_resource *res; int error; sc = (struct siba_erom *)erom; /* Allocate backing resource */ res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid, cid->enum_addr, cid->enum_addr + SIBA_ENUM_SIZE -1, SIBA_ENUM_SIZE, RF_ACTIVE|RF_SHAREABLE); if (res == NULL) return (ENOMEM); /* Initialize I/O context */ error = siba_eio_init(&sc->io, parent, res, rid, 0x0, cid->ncores); if (error) bhnd_release_resource(parent, SYS_RES_MEMORY, rid, res); return (error); } /* SIBA implementation of BHND_EROM_INIT_STATIC() */ static int siba_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid, bus_space_tag_t bst, bus_space_handle_t bsh) { struct siba_erom *sc; sc = (struct siba_erom *)erom; /* Initialize I/O context */ return (siba_eio_init_static(&sc->io, bst, bsh, 0x0, cid->ncores)); } /* SIBA implementation of BHND_EROM_FINI() */ static void siba_erom_fini(bhnd_erom_t *erom) { struct siba_erom *sc = (struct siba_erom *)erom; if (sc->io.res != NULL) { bhnd_release_resource(sc->io.dev, SYS_RES_MEMORY, sc->io.rid, sc->io.res); sc->io.res = NULL; sc->io.rid = -1; } } /* Initialize siba_erom resource I/O context */ static int siba_eio_init(struct siba_erom_io *io, device_t parent, struct bhnd_resource *res, int rid, bus_size_t offset, u_int ncores) { io->dev = parent; io->res = res; io->rid = rid; io->offset = offset; io->ncores = ncores; return (0); } /* Initialize siba_erom bus space I/O context */ static int siba_eio_init_static(struct siba_erom_io *io, bus_space_tag_t bst, bus_space_handle_t bsh, bus_size_t offset, u_int ncores) { io->res = NULL; io->rid = -1; io->bst = bst; io->bsh = bsh; io->offset = offset; io->ncores = ncores; return (0); } /** * Read a 32-bit value from @p offset relative to the base address of * the given @p core_idx. * * @param io EROM I/O context. * @param core_idx Core index. * @param offset Core register offset. */ static uint32_t siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset) { bus_size_t core_offset; /* Sanity check core index and offset */ if (core_idx >= io->ncores) panic("core index %u out of range (ncores=%u)", core_idx, io->ncores); if (offset > SIBA_CORE_SIZE - sizeof(uint32_t)) panic("invalid core offset %#jx", (uintmax_t)offset); /* Perform read */ core_offset = io->offset + SIBA_CORE_OFFSET(core_idx) + offset; if (io->res != NULL) return (bhnd_bus_read_4(io->res, core_offset)); else return (bus_space_read_4(io->bst, io->bsh, core_offset)); } /** * Read and parse identification registers for the given @p core_index. * * @param io EROM I/O context. * @param core_idx The core index. * @param unit The caller-specified unit number to be included in the return * value. */ static struct siba_core_id siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit) { uint32_t idhigh, idlow; idhigh = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH)); idlow = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW)); return (siba_parse_core_id(idhigh, idlow, core_idx, unit)); } /** * Read and parse the chip identification register from the ChipCommon core. * * @param io EROM I/O context. * @param enum_addr The physical address mapped by @p io. * @param cid On success, the parsed chip identifier. */ static int siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr, struct bhnd_chipid *cid) { struct siba_core_id ccid; uint32_t idreg; /* Identify the chipcommon core */ ccid = siba_eio_read_core_id(io, 0, 0); if (ccid.core_info.vendor != BHND_MFGID_BCM || ccid.core_info.device != BHND_COREID_CC) { if (bootverbose) { EROM_LOG(io, "first core not chipcommon " "(vendor=%#hx, core=%#hx)\n", ccid.core_info.vendor, ccid.core_info.device); } return (ENXIO); } /* Identify the chipset */ idreg = siba_eio_read_4(io, 0, CHIPC_ID); *cid = bhnd_parse_chipid(idreg, enum_addr); /* Fix up the core count in-place */ return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev, &cid->ncores)); } static int siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc, struct bhnd_core_info *core) { struct siba_erom *sc; struct bhnd_core_match imatch; sc = (struct siba_erom *)erom; /* We can't determine a core's unit number during the initial scan. */ imatch = *desc; imatch.m.match.core_unit = 0; /* Locate the first matching core */ for (u_int i = 0; i < sc->io.ncores; i++) { struct siba_core_id sid; struct bhnd_core_info ci; /* Read the core info */ sid = siba_eio_read_core_id(&sc->io, i, 0); ci = sid.core_info; /* Check for initial match */ if (!bhnd_core_matches(&ci, &imatch)) continue; /* Re-scan preceding cores to determine the unit number. */ for (u_int j = 0; j < i; j++) { sid = siba_eio_read_core_id(&sc->io, i, 0); /* Bump the unit number? */ if (sid.core_info.vendor == ci.vendor && sid.core_info.device == ci.device) ci.unit++; } /* Check for full match against now-valid unit number */ if (!bhnd_core_matches(&ci, desc)) continue; /* Matching core found */ *core = ci; return (0); } /* Not found */ return (ENOENT); } static int siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc, bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *info, bhnd_addr_t *addr, bhnd_size_t *size) { struct siba_erom *sc; struct bhnd_core_info core; struct siba_core_id sid; uint32_t am, am_addr, am_size; u_int am_offset; u_int addrspace; int error; sc = (struct siba_erom *)erom; /* Locate the requested core */ if ((error = siba_erom_lookup_core(erom, desc, &core))) return (error); /* Fetch full siba core ident */ sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit); /* Is port valid? */ if (!siba_is_port_valid(sid.num_addrspace, type, port)) return (ENOENT); /* Is region valid? */ if (region >= siba_addrspace_region_count(sid.num_addrspace, port)) return (ENOENT); /* Map the bhnd port values to a siba addrspace index */ error = siba_addrspace_index(sid.num_addrspace, type, port, region, &addrspace); if (error) return (error); /* Determine the register offset */ am_offset = siba_admatch_offset(addrspace); if (am_offset == 0) { printf("addrspace %u is unsupported", addrspace); return (ENODEV); } /* Read and parse the address match register */ am = siba_eio_read_4(&sc->io, core.core_idx, am_offset); if ((error = siba_parse_admatch(am, &am_addr, &am_size))) { printf("failed to decode address match register value 0x%x\n", am); return (error); } if (info != NULL) *info = core; *addr = am_addr; *size = am_size; return (0); } /* BHND_EROM_GET_CORE_TABLE() */ static int siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores, u_int *num_cores) { struct siba_erom *sc; struct bhnd_core_info *out; sc = (struct siba_erom *)erom; /* Allocate our core array */ out = malloc(sizeof(*out) * sc->io.ncores, M_BHND, M_NOWAIT); if (out == NULL) return (ENOMEM); *cores = out; *num_cores = sc->io.ncores; /* Enumerate all cores. */ for (u_int i = 0; i < sc->io.ncores; i++) { struct siba_core_id sid; /* Read the core info */ sid = siba_eio_read_core_id(&sc->io, i, 0); out[i] = sid.core_info; /* Determine unit number */ for (u_int j = 0; j < i; j++) { if (out[j].vendor == out[i].vendor && out[j].device == out[i].device) out[i].unit++; } } return (0); } /* BHND_EROM_FREE_CORE_TABLE() */ static void siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores) { free(cores, M_BHND); } +/* BHND_EROM_DUMP() */ +static int +siba_erom_dump(bhnd_erom_t *erom) +{ + struct siba_erom *sc; + int error; + + sc = (struct siba_erom *)erom; + + /* Enumerate all cores. */ + for (u_int i = 0; i < sc->io.ncores; i++) { + uint32_t idhigh, idlow; + uint32_t nraddr; + + idhigh = siba_eio_read_4(&sc->io, i, + SB0_REG_ABS(SIBA_CFG0_IDHIGH)); + idlow = siba_eio_read_4(&sc->io, i, + SB0_REG_ABS(SIBA_CFG0_IDLOW)); + + printf("siba core %u:\n", i); + printf("\tvendor:\t0x%04x\n", SIBA_REG_GET(idhigh, IDH_VENDOR)); + printf("\tdevice:\t0x%04x\n", SIBA_REG_GET(idhigh, IDH_DEVICE)); + printf("\trev:\t0x%04x\n", SIBA_IDH_CORE_REV(idhigh)); + printf("\tsbrev:\t0x%02x\n", SIBA_REG_GET(idlow, IDL_SBREV)); + + /* Enumerate the address match registers */ + nraddr = SIBA_REG_GET(idlow, IDL_NRADDR); + printf("\tnraddr\t0x%04x\n", nraddr); + + for (size_t addrspace = 0; addrspace < nraddr; addrspace++) { + uint32_t am, am_addr, am_size; + u_int am_offset; + + /* Determine the register offset */ + am_offset = siba_admatch_offset(addrspace); + if (am_offset == 0) { + printf("addrspace %zu unsupported", + addrspace); + break; + } + + /* Read and parse the address match register */ + am = siba_eio_read_4(&sc->io, i, am_offset); + error = siba_parse_admatch(am, &am_addr, &am_size); + if (error) { + printf("failed to decode address match " + "register value 0x%x\n", am); + continue; + } + + printf("\taddrspace %zu\n", addrspace); + printf("\t\taddr: 0x%08x\n", am_addr); + printf("\t\tsize: 0x%08x\n", am_size); + } + } + + return (0); +} + static kobj_method_t siba_erom_methods[] = { KOBJMETHOD(bhnd_erom_probe, siba_erom_probe), KOBJMETHOD(bhnd_erom_probe_static, siba_erom_probe_static), KOBJMETHOD(bhnd_erom_init, siba_erom_init), KOBJMETHOD(bhnd_erom_init_static, siba_erom_init_static), KOBJMETHOD(bhnd_erom_fini, siba_erom_fini), KOBJMETHOD(bhnd_erom_get_core_table, siba_erom_get_core_table), KOBJMETHOD(bhnd_erom_free_core_table, siba_erom_free_core_table), KOBJMETHOD(bhnd_erom_lookup_core, siba_erom_lookup_core), KOBJMETHOD(bhnd_erom_lookup_core_addr, siba_erom_lookup_core_addr), + KOBJMETHOD(bhnd_erom_dump, siba_erom_dump), KOBJMETHOD_END }; BHND_EROM_DEFINE_CLASS(siba_erom, siba_erom_parser, siba_erom_methods, sizeof(struct siba_erom)); Index: head/sys/mips/broadcom/bcm_machdep.c =================================================================== --- head/sys/mips/broadcom/bcm_machdep.c (revision 317379) +++ head/sys/mips/broadcom/bcm_machdep.c (revision 317380) @@ -1,591 +1,594 @@ /*- * Copyright (c) 2007 Bruce M. Simpson. * Copyright (c) 2016 Michael Zhilin * Copyright (c) 2016 Landon Fuller * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcm_machdep.h" #include "bcm_bmips_exts.h" #ifdef CFE #include #include #endif #if 0 #define BCM_TRACE(_fmt, ...) printf(_fmt, ##__VA_ARGS__) #else #define BCM_TRACE(_fmt, ...) #endif static int bcm_init_platform_data(struct bcm_platform *bp); static int bcm_find_core(struct bcm_platform *bp, const struct bhnd_core_match *descs, size_t num_descs, struct bhnd_core_info *info, uintptr_t *addr); static int bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize, struct bhnd_chipid *cid); extern int *edata; extern int *end; static struct bcm_platform bcm_platform_data; static bool bcm_platform_data_avail = false; #ifdef CFE static struct bcm_nvram_iocfe bcm_cfe_nvram; #endif static const struct bhnd_core_match bcm_chipc_cores[] = { { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_CC) }, { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_4706_CC) }, }; static const struct bhnd_core_match bcm_pmu_cores[] = { { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_PMU) }, }; struct bcm_platform * bcm_get_platform(void) { if (!bcm_platform_data_avail) panic("platform data not available"); return (&bcm_platform_data); } static bus_addr_t bcm_get_bus_addr(void) { long maddr; if (resource_long_value("bhnd", 0, "maddr", &maddr) == 0) return ((u_long)maddr); return (BHND_DEFAULT_CHIPC_ADDR); } /** * Search the device enumeration table for a core matching @p descs, * * @param bp Platform state containing a valid EROM parser. * @param descs The core match descriptor table. * @param num_descs The number of match descriptors in @p descs. * @param[out] info If non-NULL, will be populated with the core * info. * @param[out] addr If non-NULL, will be populated with the core's * physical register address. */ static int bcm_find_core(struct bcm_platform *bp, const struct bhnd_core_match *descs, size_t num_descs, struct bhnd_core_info *info, uintptr_t *addr) { bhnd_addr_t b_addr; bhnd_size_t b_size; int error; /* Fetch core info */ for (size_t i = 0; i < num_descs; i++) { error = bhnd_erom_lookup_core_addr(&bp->erom.obj, &descs[i], BHND_PORT_DEVICE, 0, 0, info, &b_addr, &b_size); /* Terminate search on first match */ if (error == 0) break; /* Terminate on first error (other than core not found) */ if (error != ENOENT) return (error); /* Continue search ... */ } /* Provide the core's base address */ if (addr != NULL && b_addr > UINTPTR_MAX) { BCM_ERR("core address %#jx overflows native address width\n", (uintmax_t)b_addr); return (ERANGE); } if (addr != NULL) *addr = b_addr; return (0); } /** * Read a variable directly from NVRAM, decoding as @p type. * * @param bp Platform state. * @param name The raw name of the variable to be fetched, * including any device path (/pci/1/1/varname) or * alias prefix (0:varname). * @param[out] buf On success, the requested value will be written * to this buffer. This argment may be NULL if * the value is not desired. * @param[in,out] len The capacity of @p buf. On success, will be set * to the actual size of the requested value. * @param type The data type to be written to @p buf. * * @retval 0 success * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too * small to hold the requested value. * @retval ENOENT If @p name is not found. * @retval EFTYPE If the variable data cannot be coerced to @p type. * @retval ERANGE If value coercion would overflow @p type. * @retval non-zero If parsing NVRAM otherwise fails, a regular unix error * code will be returned. */ int bcm_get_nvram(struct bcm_platform *bp, const char *name, void *buf, size_t *len, bhnd_nvram_type type) { if (bp->nvram_io == NULL || bp->nvram_cls == NULL) return (ENOENT); return (bhnd_nvram_data_getvar_direct(bp->nvram_cls, bp->nvram_io, name, buf, len, type)); } /** * Probe and attach a bhnd_erom parser instance for the bhnd bus. * * @param[out] erom_cls The probed EROM class. * @param[out] erom_ops The storage to be used when compiling * @p erom_cls. * @param[out] erom The storage to be used when initializing the * static instance of @p erom_cls. * @param esize The total available number of bytes allocated * for @p erom. If this is less than is required * by @p erom_cls ENOMEM will be returned. * @param[out] cid On success, the probed chip identification. */ static int bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize, struct bhnd_chipid *cid) { bhnd_erom_class_t **clsp; bus_space_tag_t bst; bus_space_handle_t bsh; bus_addr_t bus_addr; int error, prio, result; bus_addr = bcm_get_bus_addr(); *erom_cls = NULL; prio = 0; bst = mips_bus_space_generic; bsh = BCM_SOC_BSH(bus_addr, 0); SET_FOREACH(clsp, bhnd_erom_class_set) { struct bhnd_chipid pcid; bhnd_erom_class_t *cls; struct kobj_ops kops; cls = *clsp; /* Compile the class' ops table */ kobj_class_compile_static(cls, &kops); /* Probe the bus address */ result = bhnd_erom_probe_static(cls, bst, bsh, bus_addr, NULL, &pcid); /* Drop pointer to stack allocated ops table */ cls->ops = NULL; /* The parser did not match if an error was returned */ if (result > 0) continue; /* Check for a new highest priority match */ if (*erom_cls == NULL || result > prio) { prio = result; *cid = pcid; *erom_cls = cls; } /* Terminate immediately on BUS_PROBE_SPECIFIC */ if (result == BUS_PROBE_SPECIFIC) break; } /* Valid EROM class probed? */ if (*erom_cls == NULL) { BCM_ERR("no erom parser found for root bus at %#jx\n", (uintmax_t)bus_addr); return (ENOENT); } /* Using the provided storage, recompile the erom class ... */ kobj_class_compile_static(*erom_cls, erom_ops); /* ... and initialize the erom parser instance */ bsh = BCM_SOC_BSH(cid->enum_addr, 0); error = bhnd_erom_init_static(*erom_cls, erom, esize, cid, mips_bus_space_generic, bsh); return (error); } /** * Populate platform configuration data. */ static int bcm_init_platform_data(struct bcm_platform *bp) { bool aob, pmu; int error; #ifdef CFE /* Fetch CFE console handle (if any). Must be initialized before * any calls to printf/early_putc. */ if ((bp->cfe_console = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE)) < 0) bp->cfe_console = -1; /* Probe CFE NVRAM sources */ bp->nvram_io = &bcm_cfe_nvram.io; error = bcm_nvram_find_cfedev(&bcm_cfe_nvram, &bp->nvram_cls); if (error) { bp->nvram_io = NULL; bp->nvram_cls = NULL; } #endif /* CFE */ /* Probe and attach device table provider, populating our * chip identification */ error = bcm_erom_probe_and_attach(&bp->erom_impl, &bp->erom_ops, &bp->erom.obj, sizeof(bp->erom), &bp->cid); if (error) { BCM_ERR("error attaching erom parser: %d\n", error); return (error); } + if (bootverbose) + bhnd_erom_dump(&bp->erom.obj); + /* Fetch chipcommon core info */ error = bcm_find_core(bp, bcm_chipc_cores, nitems(bcm_chipc_cores), &bp->cc_id, &bp->cc_addr); if (error) { BCM_ERR("error locating chipc core: %d\n", error); return (error); } /* Fetch chipc capability flags */ bp->cc_caps = BCM_SOC_READ_4(bp->cc_addr, CHIPC_CAPABILITIES); bp->cc_caps_ext = 0x0; if (CHIPC_HWREV_HAS_CAP_EXT(bp->cc_id.hwrev)) bp->cc_caps_ext = BCM_CHIPC_READ_4(bp, CHIPC_CAPABILITIES_EXT); /* Fetch PMU info */ pmu = CHIPC_GET_FLAG(bp->cc_caps, CHIPC_CAP_PMU); aob = CHIPC_GET_FLAG(bp->cc_caps_ext, CHIPC_CAP2_AOB); if (pmu && aob) { /* PMU block mapped to a PMU core on the Always-on-Bus (aob) */ error = bcm_find_core(bp, bcm_pmu_cores, nitems(bcm_pmu_cores), &bp->pmu_id, &bp->pmu_addr); if (error) { BCM_ERR("error locating pmu core: %d\n", error); return (error); } } else if (pmu) { /* PMU block mapped to chipc */ bp->pmu_addr = bp->cc_addr; bp->pmu_id = bp->cc_id; } else { /* No PMU */ bp->pmu_addr = 0x0; memset(&bp->pmu_id, 0, sizeof(bp->pmu_id)); } /* Initialize PMU query state */ if (pmu) { error = bhnd_pmu_query_init(&bp->pmu, NULL, bp->cid, &bcm_pmu_soc_io, bp); if (error) { BCM_ERR("bhnd_pmu_query_init() failed: %d\n", error); return (error); } } bcm_platform_data_avail = true; return (0); } void platform_cpu_init() { /* Nothing special */ } static void mips_init(void) { int i, j; printf("entry: mips_init()\n"); #ifdef CFE /* * Query DRAM memory map from CFE. */ physmem = 0; for (i = 0; i < 10; i += 2) { int result; uint64_t addr, len, type; result = cfe_enummem(i / 2, 0, &addr, &len, &type); if (result < 0) { BCM_TRACE("There is no phys memory for: %d\n", i); phys_avail[i] = phys_avail[i + 1] = 0; break; } if (type != CFE_MI_AVAILABLE) { BCM_TRACE("phys memory is not available: %d\n", i); continue; } phys_avail[i] = addr; if (i == 0 && addr == 0) { /* * If this is the first physical memory segment probed * from CFE, omit the region at the start of physical * memory where the kernel has been loaded. */ phys_avail[i] += MIPS_KSEG0_TO_PHYS(kernel_kseg0_end); } BCM_TRACE("phys memory is available for: %d\n", i); BCM_TRACE(" => addr = %jx\n", addr); BCM_TRACE(" => len = %jd\n", len); phys_avail[i + 1] = addr + len; physmem += len; } BCM_TRACE("Total phys memory is : %ld\n", physmem); realmem = btoc(physmem); #endif for (j = 0; j < i; j++) dump_avail[j] = phys_avail[j]; physmem = realmem; init_param1(); init_param2(physmem); mips_cpu_init(); pmap_bootstrap(); mips_proc0_init(); mutex_init(); kdb_init(); #ifdef KDB if (boothowto & RB_KDB) kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger"); #endif } void platform_reset(void) { struct bcm_platform *bp; bool bcm4785war; printf("bcm::platform_reset()\n"); intr_disable(); #ifdef CFE /* Fall back on CFE if reset requested during platform * data initialization */ if (!bcm_platform_data_avail) { cfe_exit(0, 0); while (1); } #endif bp = bcm_get_platform(); bcm4785war = false; /* Handle BCM4785-specific behavior */ if (bp->cid.chip_id == BHND_CHIPID_BCM4785) { bcm4785war = true; /* Switch to async mode */ bcm_bmips_wr_pllcfg3(BMIPS_BCMCFG_PLLCFG3_SM); } /* Set watchdog (PMU or ChipCommon) */ if (bp->pmu_addr != 0x0) { BCM_PMU_WRITE_4(bp, BHND_PMU_WATCHDOG, 1); } else BCM_CHIPC_WRITE_4(bp, CHIPC_WATCHDOG, 1); /* BCM4785 */ if (bcm4785war) { mips_sync(); __asm __volatile("wait"); } while (1); } void platform_start(__register_t a0, __register_t a1, __register_t a2, __register_t a3) { vm_offset_t kernend; uint64_t platform_counter_freq; int error; /* clear the BSS and SBSS segments */ kernend = (vm_offset_t)&end; memset(&edata, 0, kernend - (vm_offset_t)(&edata)); mips_postboot_fixup(); /* Initialize pcpu stuff */ mips_pcpu0_init(); #ifdef CFE /* * Initialize CFE firmware trampolines. This must be done * before any CFE APIs are called, including writing * to the CFE console. * * CFE passes the following values in registers: * a0: firmware handle * a2: firmware entry point * a3: entry point seal */ if (a3 == CFE_EPTSEAL) cfe_init(a0, a2); #endif /* Init BCM platform data */ if ((error = bcm_init_platform_data(&bcm_platform_data))) panic("bcm_init_platform_data() failed: %d", error); platform_counter_freq = bcm_get_cpufreq(bcm_get_platform()); /* CP0 ticks every two cycles */ mips_timer_early_init(platform_counter_freq / 2); cninit(); mips_init(); mips_timer_init_params(platform_counter_freq, 1); } /* * CFE-based EARLY_PRINTF support. To use, add the following to the kernel * config: * option EARLY_PRINTF * option CFE * device cfe */ #if defined(EARLY_PRINTF) && defined(CFE) static void bcm_cfe_eputc(int c) { unsigned char ch; int handle; ch = (unsigned char) c; /* bcm_get_platform() cannot be used here, as we may be called * from bcm_init_platform_data(). */ if ((handle = bcm_platform_data.cfe_console) < 0) return; if (ch == '\n') early_putc('\r'); while ((cfe_write(handle, &ch, 1)) == 0) continue; } early_putc_t *early_putc = bcm_cfe_eputc; #endif /* EARLY_PRINTF */