1296077Sadrian/*- 2296077Sadrian * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3296077Sadrian * All rights reserved. 4296077Sadrian * 5296077Sadrian * Redistribution and use in source and binary forms, with or without 6296077Sadrian * modification, are permitted provided that the following conditions 7296077Sadrian * are met: 8296077Sadrian * 1. Redistributions of source code must retain the above copyright 9296077Sadrian * notice, this list of conditions and the following disclaimer, 10296077Sadrian * without modification. 11296077Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12296077Sadrian * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13296077Sadrian * redistribution must be conditioned upon including a substantially 14296077Sadrian * similar Disclaimer requirement for further binary redistribution. 15296077Sadrian * 16296077Sadrian * NO WARRANTY 17296077Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18296077Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19296077Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20296077Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21296077Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22296077Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23296077Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24296077Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25296077Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26296077Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27296077Sadrian * THE POSSIBILITY OF SUCH DAMAGES. 28296077Sadrian */ 29296077Sadrian 30296077Sadrian#include <sys/cdefs.h> 31296077Sadrian__FBSDID("$FreeBSD: releng/11.0/sys/dev/bhnd/bcma/bcma_erom.c 299314 2016-05-10 04:55:57Z adrian $"); 32296077Sadrian 33296077Sadrian#include <sys/param.h> 34296077Sadrian#include <sys/bus.h> 35296077Sadrian#include <sys/kernel.h> 36296077Sadrian#include <sys/limits.h> 37296077Sadrian#include <sys/systm.h> 38296077Sadrian 39296077Sadrian#include <machine/bus.h> 40296077Sadrian#include <machine/resource.h> 41296077Sadrian 42296077Sadrian#include "bcma_eromreg.h" 43296077Sadrian#include "bcma_eromvar.h" 44296077Sadrian 45296077Sadrian/* 46296077Sadrian * BCMA Enumeration ROM (EROM) Table 47296077Sadrian * 48296077Sadrian * Provides auto-discovery of BCMA cores on Broadcom's HND SoC. 49296077Sadrian * 50296077Sadrian * The EROM core address can be found at BCMA_CC_EROM_ADDR within the 51296077Sadrian * ChipCommon registers. The table itself is comprised of 32-bit 52296077Sadrian * type-tagged entries, organized into an array of variable-length 53296077Sadrian * core descriptor records. 54296077Sadrian * 55296077Sadrian * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF) 56296077Sadrian * marker. 57296077Sadrian */ 58296077Sadrian 59296077Sadrianstatic const char *erom_entry_type_name (uint8_t entry); 60296077Sadrianstatic int erom_read32(struct bcma_erom *erom, uint32_t *entry); 61296077Sadrianstatic int erom_skip32(struct bcma_erom *erom); 62296077Sadrian 63296077Sadrianstatic int erom_skip_core(struct bcma_erom *erom); 64296077Sadrianstatic int erom_skip_mport(struct bcma_erom *erom); 65296077Sadrianstatic int erom_skip_sport_region(struct bcma_erom *erom); 66296077Sadrian 67296077Sadrianstatic int erom_seek_next(struct bcma_erom *erom, uint8_t etype); 68296077Sadrian 69296077Sadrian#define EROM_LOG(erom, fmt, ...) \ 70296077Sadrian device_printf(erom->dev, "erom[0x%llx]: " fmt, \ 71296077Sadrian (unsigned long long) (erom->offset), ##__VA_ARGS__); 72296077Sadrian 73296077Sadrian/** 74296077Sadrian * Open an EROM table for reading. 75296077Sadrian * 76296077Sadrian * @param[out] erom On success, will be populated with a valid EROM 77296077Sadrian * read state. 78296077Sadrian * @param r An active resource mapping the EROM core. 79296077Sadrian * @param offset Offset of the EROM core within @p resource. 80296077Sadrian * 81296077Sadrian * @retval 0 success 82296077Sadrian * @retval non-zero if the erom table could not be opened. 83296077Sadrian */ 84296077Sadrianint 85296077Sadrianbcma_erom_open(struct bcma_erom *erom, struct resource *r, bus_size_t offset) 86296077Sadrian{ 87296077Sadrian /* Initialize the EROM reader */ 88296077Sadrian erom->dev = rman_get_device(r); 89296077Sadrian erom->r = r; 90296077Sadrian erom->start = offset + BCMA_EROM_TABLE_START; 91296077Sadrian erom->offset = 0; 92296077Sadrian 93296077Sadrian return (0); 94296077Sadrian} 95296077Sadrian 96296077Sadrian/** Return the type name for an EROM entry */ 97296077Sadrianstatic const char * 98296077Sadrianerom_entry_type_name (uint8_t entry) 99296077Sadrian{ 100296077Sadrian switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { 101296077Sadrian case BCMA_EROM_ENTRY_TYPE_CORE: 102296077Sadrian return "core"; 103296077Sadrian case BCMA_EROM_ENTRY_TYPE_MPORT: 104296077Sadrian return "mport"; 105296077Sadrian case BCMA_EROM_ENTRY_TYPE_REGION: 106296077Sadrian return "region"; 107296077Sadrian default: 108296077Sadrian return "unknown"; 109296077Sadrian } 110296077Sadrian} 111296077Sadrian 112296077Sadrian/** 113296077Sadrian * Return the current read position. 114296077Sadrian */ 115296077Sadrianbus_size_t 116296077Sadrianbcma_erom_tell(struct bcma_erom *erom) 117296077Sadrian{ 118296077Sadrian return (erom->offset); 119296077Sadrian} 120296077Sadrian 121296077Sadrian/** 122296077Sadrian * Seek to an absolute read position. 123296077Sadrian */ 124296077Sadrianvoid 125296077Sadrianbcma_erom_seek(struct bcma_erom *erom, bus_size_t offset) 126296077Sadrian{ 127296077Sadrian erom->offset = offset; 128296077Sadrian} 129296077Sadrian 130296077Sadrian/** 131296077Sadrian * Read a 32-bit entry value from the EROM table without advancing the 132296077Sadrian * read position. 133296077Sadrian * 134296077Sadrian * @param erom EROM read state. 135296077Sadrian * @param entry Will contain the read result on success. 136296077Sadrian * @retval 0 success 137296077Sadrian * @retval ENOENT The end of the EROM table was reached. 138296077Sadrian * @retval non-zero The read could not be completed. 139296077Sadrian */ 140296077Sadrianint 141296077Sadrianbcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry) 142296077Sadrian{ 143296077Sadrian if (erom->offset >= BCMA_EROM_TABLE_SIZE) { 144296077Sadrian EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n"); 145296077Sadrian return (EINVAL); 146296077Sadrian } 147296077Sadrian 148296077Sadrian *entry = bus_read_4(erom->r, erom->start + erom->offset); 149296077Sadrian return (0); 150296077Sadrian} 151296077Sadrian 152296077Sadrian/** 153296077Sadrian * Read a 32-bit entry value from the EROM table. 154296077Sadrian * 155296077Sadrian * @param erom EROM read state. 156296077Sadrian * @param entry Will contain the read result on success. 157296077Sadrian * @retval 0 success 158296077Sadrian * @retval ENOENT The end of the EROM table was reached. 159296077Sadrian * @retval non-zero The read could not be completed. 160296077Sadrian */ 161296077Sadrianstatic int 162296077Sadrianerom_read32(struct bcma_erom *erom, uint32_t *entry) 163296077Sadrian{ 164296077Sadrian int error; 165296077Sadrian 166296077Sadrian if ((error = bcma_erom_peek32(erom, entry)) == 0) 167296077Sadrian erom->offset += 4; 168296077Sadrian 169296077Sadrian return (error); 170296077Sadrian} 171296077Sadrian 172296077Sadrian/** 173296077Sadrian * Read and discard 32-bit entry value from the EROM table. 174296077Sadrian * 175296077Sadrian * @param erom EROM read state. 176296077Sadrian * @retval 0 success 177296077Sadrian * @retval ENOENT The end of the EROM table was reached. 178296077Sadrian * @retval non-zero The read could not be completed. 179296077Sadrian */ 180296077Sadrianstatic int 181296077Sadrianerom_skip32(struct bcma_erom *erom) 182296077Sadrian{ 183296077Sadrian uint32_t entry; 184296077Sadrian 185296077Sadrian return erom_read32(erom, &entry); 186296077Sadrian} 187296077Sadrian 188296077Sadrian/** 189296077Sadrian * Read and discard a core descriptor from the EROM table. 190296077Sadrian * 191296077Sadrian * @param erom EROM read state. 192296077Sadrian * @retval 0 success 193296077Sadrian * @retval ENOENT The end of the EROM table was reached. 194296077Sadrian * @retval non-zero The read could not be completed. 195296077Sadrian */ 196296077Sadrianstatic int 197296077Sadrianerom_skip_core(struct bcma_erom *erom) 198296077Sadrian{ 199296077Sadrian struct bcma_erom_core core; 200296077Sadrian return (bcma_erom_parse_core(erom, &core)); 201296077Sadrian} 202296077Sadrian 203296077Sadrian/** 204296077Sadrian * Read and discard a master port descriptor from the EROM table. 205296077Sadrian * 206296077Sadrian * @param erom EROM read state. 207296077Sadrian * @retval 0 success 208296077Sadrian * @retval ENOENT The end of the EROM table was reached. 209296077Sadrian * @retval non-zero The read could not be completed. 210296077Sadrian */ 211296077Sadrianstatic int 212296077Sadrianerom_skip_mport(struct bcma_erom *erom) 213296077Sadrian{ 214296077Sadrian struct bcma_erom_mport mp; 215296077Sadrian return (bcma_erom_parse_mport(erom, &mp)); 216296077Sadrian} 217296077Sadrian 218296077Sadrian/** 219296077Sadrian * Read and discard a port region descriptor from the EROM table. 220296077Sadrian * 221296077Sadrian * @param erom EROM read state. 222296077Sadrian * @retval 0 success 223296077Sadrian * @retval ENOENT The end of the EROM table was reached. 224296077Sadrian * @retval non-zero The read could not be completed. 225296077Sadrian */ 226296077Sadrianstatic int 227296077Sadrianerom_skip_sport_region(struct bcma_erom *erom) 228296077Sadrian{ 229296077Sadrian struct bcma_erom_sport_region r; 230296077Sadrian return (bcma_erom_parse_sport_region(erom, &r)); 231296077Sadrian} 232296077Sadrian 233296077Sadrian/** 234296077Sadrian * Seek to the next entry matching the given EROM entry type. 235296077Sadrian * 236296077Sadrian * @param erom EROM read state. 237296077Sadrian * @param etype One of BCMA_EROM_ENTRY_TYPE_CORE, 238296077Sadrian * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION. 239296077Sadrian * @retval 0 success 240296077Sadrian * @retval ENOENT The end of the EROM table was reached. 241296077Sadrian * @retval non-zero Reading or parsing the descriptor failed. 242296077Sadrian */ 243296077Sadrianstatic int 244296077Sadrianerom_seek_next(struct bcma_erom *erom, uint8_t etype) 245296077Sadrian{ 246296077Sadrian uint32_t entry; 247296077Sadrian int error; 248296077Sadrian 249296077Sadrian /* Iterate until we hit an entry matching the requested type. */ 250296077Sadrian while (!(error = bcma_erom_peek32(erom, &entry))) { 251296077Sadrian /* Handle EOF */ 252296077Sadrian if (entry == BCMA_EROM_TABLE_EOF) 253296077Sadrian return (ENOENT); 254296077Sadrian 255296077Sadrian /* Invalid entry */ 256296077Sadrian if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) 257296077Sadrian return (EINVAL); 258296077Sadrian 259296077Sadrian /* Entry type matches? */ 260296077Sadrian if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype) 261296077Sadrian return (0); 262296077Sadrian 263296077Sadrian /* Skip non-matching entry types. */ 264296077Sadrian switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) { 265296077Sadrian case BCMA_EROM_ENTRY_TYPE_CORE: 266296077Sadrian if ((error = erom_skip_core(erom))) 267296077Sadrian return (error); 268296077Sadrian 269296077Sadrian break; 270296077Sadrian 271296077Sadrian case BCMA_EROM_ENTRY_TYPE_MPORT: 272296077Sadrian if ((error = erom_skip_mport(erom))) 273296077Sadrian return (error); 274296077Sadrian 275296077Sadrian break; 276296077Sadrian 277296077Sadrian case BCMA_EROM_ENTRY_TYPE_REGION: 278296077Sadrian if ((error = erom_skip_sport_region(erom))) 279296077Sadrian return (error); 280296077Sadrian break; 281296077Sadrian 282296077Sadrian default: 283296077Sadrian /* Unknown entry type! */ 284296077Sadrian return (EINVAL); 285296077Sadrian } 286296077Sadrian } 287296077Sadrian 288296077Sadrian return (error); 289296077Sadrian} 290296077Sadrian 291296077Sadrian/** 292296077Sadrian * Return the read position to the start of the EROM table. 293296077Sadrian * 294296077Sadrian * @param erom EROM read state. 295296077Sadrian */ 296296077Sadrianvoid 297296077Sadrianbcma_erom_reset(struct bcma_erom *erom) 298296077Sadrian{ 299296077Sadrian erom->offset = 0; 300296077Sadrian} 301296077Sadrian 302296077Sadrian/** 303296077Sadrian * Seek to the requested core entry. 304296077Sadrian * 305296077Sadrian * @param erom EROM read state. 306296077Sadrian * @param core_index Index of the core to seek to. 307296077Sadrian * @retval 0 success 308296077Sadrian * @retval ENOENT The end of the EROM table was reached before @p index was 309296077Sadrian * found. 310296077Sadrian * @retval non-zero Reading or parsing failed. 311296077Sadrian */ 312296077Sadrianint 313296077Sadrianbcma_erom_seek_core_index(struct bcma_erom *erom, u_int core_index) 314296077Sadrian{ 315296077Sadrian int error; 316296077Sadrian 317296077Sadrian /* Start search at top of EROM */ 318296077Sadrian bcma_erom_reset(erom); 319296077Sadrian 320296077Sadrian /* Skip core descriptors till we hit the requested entry */ 321296077Sadrian for (u_int i = 0; i < core_index; i++) { 322296077Sadrian struct bcma_erom_core core; 323296077Sadrian 324296077Sadrian /* Read past the core descriptor */ 325296077Sadrian if ((error = bcma_erom_parse_core(erom, &core))) 326296077Sadrian return (error); 327296077Sadrian 328296077Sadrian /* Seek to the next readable core entry */ 329296077Sadrian error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); 330296077Sadrian if (error) 331296077Sadrian return (error); 332296077Sadrian } 333296077Sadrian 334296077Sadrian return (0); 335296077Sadrian} 336296077Sadrian 337296077Sadrian 338296077Sadrian/** 339296077Sadrian * Read the next core descriptor from the EROM table. 340296077Sadrian * 341296077Sadrian * @param erom EROM read state. 342296077Sadrian * @param[out] core On success, will be populated with the parsed core 343296077Sadrian * descriptor data. 344296077Sadrian * @retval 0 success 345296077Sadrian * @retval ENOENT The end of the EROM table was reached. 346296077Sadrian * @retval non-zero Reading or parsing the core descriptor failed. 347296077Sadrian */ 348296077Sadrianint 349296077Sadrianbcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core) 350296077Sadrian{ 351296077Sadrian uint32_t entry; 352296077Sadrian int error; 353296077Sadrian 354296077Sadrian /* Parse CoreDescA */ 355296077Sadrian if ((error = erom_read32(erom, &entry))) 356296077Sadrian return (error); 357296077Sadrian 358296077Sadrian /* Handle EOF */ 359296077Sadrian if (entry == BCMA_EROM_TABLE_EOF) 360296077Sadrian return (ENOENT); 361296077Sadrian 362296077Sadrian if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { 363296077Sadrian EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n", 364296077Sadrian entry, erom_entry_type_name(entry)); 365296077Sadrian 366296077Sadrian return (EINVAL); 367296077Sadrian } 368296077Sadrian 369296077Sadrian core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER); 370296077Sadrian core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID); 371296077Sadrian 372296077Sadrian /* Parse CoreDescB */ 373296077Sadrian if ((error = erom_read32(erom, &entry))) 374296077Sadrian return (error); 375296077Sadrian 376296077Sadrian if (!BCMA_EROM_ENTRY_IS(entry, CORE)) { 377296077Sadrian return (EINVAL); 378296077Sadrian } 379296077Sadrian 380296077Sadrian core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV); 381296077Sadrian core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP); 382296077Sadrian core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP); 383296077Sadrian core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP); 384296077Sadrian core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP); 385296077Sadrian 386296077Sadrian return (0); 387296077Sadrian} 388296077Sadrian 389296077Sadrian/** 390296077Sadrian * Read the next master port descriptor from the EROM table. 391296077Sadrian * 392296077Sadrian * @param erom EROM read state. 393296077Sadrian * @param[out] mport On success, will be populated with the parsed 394296077Sadrian * descriptor data. 395296077Sadrian * @retval 0 success 396296077Sadrian * @retval non-zero Reading or parsing the descriptor failed. 397296077Sadrian */ 398296077Sadrianint 399296077Sadrianbcma_erom_parse_mport(struct bcma_erom *erom, 400296077Sadrian struct bcma_erom_mport *mport) 401296077Sadrian{ 402296077Sadrian uint32_t entry; 403296077Sadrian int error; 404296077Sadrian 405296077Sadrian /* Parse the master port descriptor */ 406296077Sadrian if ((error = erom_read32(erom, &entry))) 407296077Sadrian return (error); 408296077Sadrian 409296077Sadrian if (!BCMA_EROM_ENTRY_IS(entry, MPORT)) 410296077Sadrian return (EINVAL); 411296077Sadrian 412296077Sadrian mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID); 413296077Sadrian mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM); 414296077Sadrian 415296077Sadrian return (0); 416296077Sadrian} 417296077Sadrian 418296077Sadrian/** 419296077Sadrian * Read the next slave port region descriptor from the EROM table. 420296077Sadrian * 421296077Sadrian * @param erom EROM read state. 422296077Sadrian * @param[out] mport On success, will be populated with the parsed 423296077Sadrian * descriptor data. 424296077Sadrian * @retval 0 success 425296077Sadrian * @retval ENOENT The end of the region descriptor table was reached. 426296077Sadrian * @retval non-zero Reading or parsing the descriptor failed. 427296077Sadrian */ 428296077Sadrianint 429296077Sadrianbcma_erom_parse_sport_region(struct bcma_erom *erom, 430296077Sadrian struct bcma_erom_sport_region *region) 431296077Sadrian{ 432296077Sadrian uint32_t entry; 433296077Sadrian uint8_t size_type; 434296077Sadrian int error; 435296077Sadrian 436296077Sadrian /* Peek at the region descriptor */ 437296077Sadrian if (bcma_erom_peek32(erom, &entry)) 438296077Sadrian return (EINVAL); 439296077Sadrian 440296077Sadrian /* A non-region entry signals the end of the region table */ 441296077Sadrian if (!BCMA_EROM_ENTRY_IS(entry, REGION)) { 442296077Sadrian return (ENOENT); 443296077Sadrian } else { 444296077Sadrian erom_skip32(erom); 445296077Sadrian } 446296077Sadrian 447296077Sadrian region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE); 448296077Sadrian region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE); 449296077Sadrian region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT); 450296077Sadrian size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE); 451296077Sadrian 452296077Sadrian /* If region address is 64-bit, fetch the high bits. */ 453296077Sadrian if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) { 454296077Sadrian if ((error = erom_read32(erom, &entry))) 455296077Sadrian return (error); 456296077Sadrian 457296077Sadrian region->base_addr |= ((bhnd_addr_t) entry << 32); 458296077Sadrian } 459296077Sadrian 460296077Sadrian /* Parse the region size; it's either encoded as the binary logarithm 461296077Sadrian * of the number of 4K pages (i.e. log2 n), or its encoded as a 462296077Sadrian * 32-bit/64-bit literal value directly following the current entry. */ 463296077Sadrian if (size_type == BCMA_EROM_REGION_SIZE_OTHER) { 464296077Sadrian if ((error = erom_read32(erom, &entry))) 465296077Sadrian return (error); 466296077Sadrian 467296077Sadrian region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL); 468296077Sadrian 469296077Sadrian if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) { 470296077Sadrian if ((error = erom_read32(erom, &entry))) 471296077Sadrian return (error); 472296077Sadrian region->size |= ((bhnd_size_t) entry << 32); 473296077Sadrian } 474296077Sadrian } else { 475296077Sadrian region->size = BCMA_EROM_REGION_SIZE_BASE << size_type; 476296077Sadrian } 477296077Sadrian 478296077Sadrian /* Verify that addr+size does not overflow. */ 479296077Sadrian if (region->size != 0 && 480296077Sadrian BHND_ADDR_MAX - (region->size - 1) < region->base_addr) 481296077Sadrian { 482296077Sadrian EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n", 483296077Sadrian erom_entry_type_name(region->region_type), 484296077Sadrian region->region_port, 485296077Sadrian (unsigned long long) region->base_addr, 486296077Sadrian (unsigned long long) region->size); 487296077Sadrian 488296077Sadrian return (EINVAL); 489296077Sadrian } 490296077Sadrian 491296077Sadrian return (0); 492296077Sadrian} 493296077Sadrian 494296077Sadrian/** 495296077Sadrian * Parse all cores descriptors from @p erom and return the array 496296077Sadrian * in @p cores and the count in @p num_cores. The current EROM read position 497296077Sadrian * is left unmodified. 498296077Sadrian * 499296077Sadrian * The memory allocated for the table should be freed using 500296077Sadrian * `free(*cores, M_BHND)`. @p cores and @p num_cores are not changed 501296077Sadrian * when an error is returned. 502296077Sadrian * 503296077Sadrian * @param erom EROM read state. 504296077Sadrian * @param[out] cores the table of parsed core descriptors. 505296077Sadrian * @param[out] num_cores the number of core records in @p cores. 506296077Sadrian */ 507296077Sadrianint 508296077Sadrianbcma_erom_get_core_info(struct bcma_erom *erom, 509296077Sadrian struct bhnd_core_info **cores, 510296077Sadrian u_int *num_cores) 511296077Sadrian{ 512296077Sadrian struct bhnd_core_info *buffer; 513296077Sadrian bus_size_t initial_offset; 514296077Sadrian u_int count; 515296077Sadrian int error; 516296077Sadrian 517296077Sadrian buffer = NULL; 518296077Sadrian initial_offset = bcma_erom_tell(erom); 519296077Sadrian 520296077Sadrian /* Determine the core count */ 521296077Sadrian bcma_erom_reset(erom); 522296077Sadrian for (count = 0, error = 0; !error; count++) { 523296077Sadrian struct bcma_erom_core core; 524296077Sadrian 525296077Sadrian /* Seek to the first readable core entry */ 526296077Sadrian error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); 527296077Sadrian if (error == ENOENT) 528296077Sadrian break; 529296077Sadrian else if (error) 530296077Sadrian goto cleanup; 531296077Sadrian 532296077Sadrian /* Read past the core descriptor */ 533296077Sadrian if ((error = bcma_erom_parse_core(erom, &core))) 534296077Sadrian goto cleanup; 535296077Sadrian } 536296077Sadrian 537296077Sadrian /* Allocate our output buffer */ 538296077Sadrian buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND, 539296077Sadrian M_NOWAIT); 540296077Sadrian if (buffer == NULL) { 541296077Sadrian error = ENOMEM; 542296077Sadrian goto cleanup; 543296077Sadrian } 544296077Sadrian 545296077Sadrian /* Parse all core descriptors */ 546296077Sadrian bcma_erom_reset(erom); 547296077Sadrian for (u_int i = 0; i < count; i++) { 548296077Sadrian struct bcma_erom_core core; 549296077Sadrian 550296077Sadrian /* Parse the core */ 551296077Sadrian error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE); 552296077Sadrian if (error) 553296077Sadrian goto cleanup; 554296077Sadrian 555296077Sadrian error = bcma_erom_parse_core(erom, &core); 556296077Sadrian if (error) 557296077Sadrian goto cleanup; 558296077Sadrian 559296077Sadrian /* Convert to a bhnd info record */ 560296077Sadrian buffer[i].vendor = core.vendor; 561296077Sadrian buffer[i].device = core.device; 562296077Sadrian buffer[i].hwrev = core.rev; 563296077Sadrian buffer[i].core_idx = i; 564296077Sadrian buffer[i].unit = 0; 565296077Sadrian 566296077Sadrian /* Determine the unit number */ 567296077Sadrian for (u_int j = 0; j < i; j++) { 568296077Sadrian if (buffer[i].vendor == buffer[j].vendor && 569296077Sadrian buffer[i].device == buffer[j].device) 570297793Spfg buffer[i].unit++; 571296077Sadrian } 572296077Sadrian } 573296077Sadrian 574296077Sadriancleanup: 575296077Sadrian if (!error) { 576296077Sadrian *cores = buffer; 577296077Sadrian *num_cores = count; 578296077Sadrian } else { 579296077Sadrian if (buffer != NULL) 580296077Sadrian free(buffer, M_BHND); 581296077Sadrian } 582296077Sadrian 583296077Sadrian /* Restore the initial position */ 584296077Sadrian bcma_erom_seek(erom, initial_offset); 585296077Sadrian return (error); 586296077Sadrian} 587296077Sadrian 588296077Sadrian 589296077Sadrian/** 590296077Sadrian * Register all MMIO region descriptors for the given slave port. 591296077Sadrian * 592296077Sadrian * @param erom EROM read state. 593296077Sadrian * @param corecfg Core info to be populated with the scanned port regions. 594296077Sadrian * @param port_num Port index for which regions will be parsed. 595296077Sadrian * @param region_type The region type to be parsed. 596296077Sadrian * @param[out] offset The offset at which to perform parsing. On success, this 597296077Sadrian * will be updated to point to the next EROM table entry. 598296077Sadrian */ 599296077Sadrianstatic int 600296077Sadrianerom_corecfg_fill_port_regions(struct bcma_erom *erom, 601296077Sadrian struct bcma_corecfg *corecfg, bcma_pid_t port_num, 602296077Sadrian uint8_t region_type) 603296077Sadrian{ 604296077Sadrian struct bcma_sport *sport; 605296077Sadrian struct bcma_sport_list *sports; 606296077Sadrian bus_size_t entry_offset; 607296077Sadrian int error; 608296077Sadrian bhnd_port_type port_type; 609296077Sadrian 610296077Sadrian error = 0; 611296077Sadrian 612296077Sadrian /* Determine the port type for this region type. */ 613296077Sadrian switch (region_type) { 614296077Sadrian case BCMA_EROM_REGION_TYPE_DEVICE: 615296077Sadrian port_type = BHND_PORT_DEVICE; 616296077Sadrian break; 617296077Sadrian case BCMA_EROM_REGION_TYPE_BRIDGE: 618296077Sadrian port_type = BHND_PORT_BRIDGE; 619296077Sadrian break; 620296077Sadrian case BCMA_EROM_REGION_TYPE_MWRAP: 621296077Sadrian case BCMA_EROM_REGION_TYPE_SWRAP: 622296077Sadrian port_type = BHND_PORT_AGENT; 623296077Sadrian break; 624296077Sadrian default: 625296077Sadrian EROM_LOG(erom, "unsupported region type %hhx\n", 626296077Sadrian region_type); 627296077Sadrian return (EINVAL); 628297793Spfg } 629296077Sadrian 630296077Sadrian /* Fetch the list to be populated */ 631296077Sadrian sports = bcma_corecfg_get_port_list(corecfg, port_type); 632296077Sadrian 633296077Sadrian /* Allocate a new port descriptor */ 634296077Sadrian sport = bcma_alloc_sport(port_num, port_type); 635296077Sadrian if (sport == NULL) 636296077Sadrian return (ENOMEM); 637296077Sadrian 638296077Sadrian /* Read all address regions defined for this port */ 639296077Sadrian for (bcma_rmid_t region_num = 0;; region_num++) { 640296077Sadrian struct bcma_map *map; 641296077Sadrian struct bcma_erom_sport_region spr; 642296077Sadrian 643296077Sadrian /* No valid port definition should come anywhere near 644296077Sadrian * BCMA_RMID_MAX. */ 645296077Sadrian if (region_num == BCMA_RMID_MAX) { 646296077Sadrian EROM_LOG(erom, "core%u %s%u: region count reached " 647296077Sadrian "upper limit of %u\n", 648296077Sadrian corecfg->core_info.core_idx, 649296077Sadrian bhnd_port_type_name(port_type), 650296077Sadrian port_num, BCMA_RMID_MAX); 651296077Sadrian 652296077Sadrian error = EINVAL; 653296077Sadrian goto cleanup; 654296077Sadrian } 655296077Sadrian 656296077Sadrian /* Parse the next region entry. */ 657296077Sadrian entry_offset = bcma_erom_tell(erom); 658296077Sadrian error = bcma_erom_parse_sport_region(erom, &spr); 659296077Sadrian if (error && error != ENOENT) { 660296077Sadrian EROM_LOG(erom, "core%u %s%u.%u: invalid slave port " 661296077Sadrian "address region\n", 662296077Sadrian corecfg->core_info.core_idx, 663296077Sadrian bhnd_port_type_name(port_type), 664296077Sadrian port_num, region_num); 665296077Sadrian goto cleanup; 666296077Sadrian } 667296077Sadrian 668296077Sadrian /* ENOENT signals no further region entries */ 669296077Sadrian if (error == ENOENT) { 670296077Sadrian /* No further entries */ 671296077Sadrian error = 0; 672296077Sadrian break; 673296077Sadrian } 674296077Sadrian 675296077Sadrian /* A region or type mismatch also signals no further region 676296077Sadrian * entries */ 677296077Sadrian if (spr.region_port != port_num || 678296077Sadrian spr.region_type != region_type) 679296077Sadrian { 680296077Sadrian /* We don't want to consume this entry */ 681296077Sadrian bcma_erom_seek(erom, entry_offset); 682296077Sadrian 683296077Sadrian error = 0; 684296077Sadrian goto cleanup; 685296077Sadrian } 686296077Sadrian 687296077Sadrian /* 688296077Sadrian * Create the map entry. 689296077Sadrian */ 690296077Sadrian map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT); 691296077Sadrian if (map == NULL) { 692296077Sadrian error = ENOMEM; 693296077Sadrian goto cleanup; 694296077Sadrian } 695296077Sadrian 696296077Sadrian map->m_region_num = region_num; 697296077Sadrian map->m_base = spr.base_addr; 698296077Sadrian map->m_size = spr.size; 699296077Sadrian map->m_rid = -1; 700296077Sadrian 701296077Sadrian /* Add the region map to the port */ 702296077Sadrian STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link); 703296077Sadrian sport->sp_num_maps++; 704296077Sadrian } 705296077Sadrian 706296077Sadriancleanup: 707296077Sadrian /* Append the new port descriptor on success, or deallocate the 708296077Sadrian * partially parsed descriptor on failure. */ 709296077Sadrian if (error == 0) { 710296077Sadrian STAILQ_INSERT_TAIL(sports, sport, sp_link); 711296077Sadrian } else if (sport != NULL) { 712296077Sadrian bcma_free_sport(sport); 713296077Sadrian } 714296077Sadrian 715296077Sadrian return error; 716296077Sadrian} 717296077Sadrian 718296077Sadrian/** 719296077Sadrian * Parse the next core entry from the EROM table and produce a bcma_corecfg 720296077Sadrian * to be owned by the caller. 721296077Sadrian * 722296077Sadrian * @param erom EROM read state. 723296077Sadrian * @param[out] result On success, the core's device info. The caller inherits 724296077Sadrian * ownership of this allocation. 725296077Sadrian * 726296077Sadrian * @return If successful, returns 0. If the end of the EROM table is hit, 727296077Sadrian * ENOENT will be returned. On error, returns a non-zero error value. 728296077Sadrian */ 729296077Sadrianint 730296077Sadrianbcma_erom_parse_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result) 731296077Sadrian{ 732296077Sadrian struct bcma_corecfg *cfg; 733296077Sadrian struct bcma_erom_core core; 734296077Sadrian uint8_t first_region_type; 735296077Sadrian bus_size_t initial_offset; 736296077Sadrian u_int core_index; 737296077Sadrian int core_unit; 738296077Sadrian int error; 739296077Sadrian 740296077Sadrian cfg = NULL; 741296077Sadrian initial_offset = bcma_erom_tell(erom); 742296077Sadrian 743296077Sadrian /* Parse the next core entry */ 744296077Sadrian if ((error = bcma_erom_parse_core(erom, &core))) 745296077Sadrian return (error); 746296077Sadrian 747296077Sadrian /* Determine the core's index and unit numbers */ 748296077Sadrian bcma_erom_reset(erom); 749296077Sadrian core_unit = 0; 750296077Sadrian core_index = 0; 751296077Sadrian for (; bcma_erom_tell(erom) != initial_offset; core_index++) { 752296077Sadrian struct bcma_erom_core prev_core; 753296077Sadrian 754296077Sadrian /* Parse next core */ 755296077Sadrian if ((error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE))) 756296077Sadrian return (error); 757296077Sadrian 758296077Sadrian if ((error = bcma_erom_parse_core(erom, &prev_core))) 759296077Sadrian return (error); 760296077Sadrian 761296077Sadrian /* Is earlier unit? */ 762296077Sadrian if (core.vendor == prev_core.vendor && 763296077Sadrian core.device == prev_core.device) 764296077Sadrian { 765296077Sadrian core_unit++; 766296077Sadrian } 767296077Sadrian 768296077Sadrian /* Seek to next core */ 769296077Sadrian if ((error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE))) 770296077Sadrian return (error); 771296077Sadrian } 772296077Sadrian 773296077Sadrian /* We already parsed the core descriptor */ 774296077Sadrian if ((error = erom_skip_core(erom))) 775296077Sadrian return (error); 776296077Sadrian 777296077Sadrian /* Allocate our corecfg */ 778296077Sadrian cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor, 779296077Sadrian core.device, core.rev); 780296077Sadrian if (cfg == NULL) 781296077Sadrian return (ENOMEM); 782296077Sadrian 783296077Sadrian /* These are 5-bit values in the EROM table, and should never be able 784296077Sadrian * to overflow BCMA_PID_MAX. */ 785296077Sadrian KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count")); 786296077Sadrian KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count")); 787296077Sadrian KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX, 788296077Sadrian ("unsupported wport count")); 789296077Sadrian 790296077Sadrian if (bootverbose) { 791296077Sadrian EROM_LOG(erom, 792296077Sadrian "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n", 793296077Sadrian core_index, 794296077Sadrian bhnd_vendor_name(core.vendor), 795296077Sadrian bhnd_find_core_name(core.vendor, core.device), 796296077Sadrian core.device, core.rev, core_unit); 797296077Sadrian } 798296077Sadrian 799296077Sadrian cfg->num_master_ports = core.num_mport; 800296077Sadrian cfg->num_dev_ports = 0; /* determined below */ 801296077Sadrian cfg->num_bridge_ports = 0; /* determined blow */ 802296077Sadrian cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap; 803296077Sadrian 804296077Sadrian /* Parse Master Port Descriptors */ 805296077Sadrian for (uint8_t i = 0; i < core.num_mport; i++) { 806296077Sadrian struct bcma_mport *mport; 807296077Sadrian struct bcma_erom_mport mpd; 808296077Sadrian 809296077Sadrian /* Parse the master port descriptor */ 810296077Sadrian error = bcma_erom_parse_mport(erom, &mpd); 811296077Sadrian if (error) 812296077Sadrian goto failed; 813296077Sadrian 814296077Sadrian /* Initialize a new bus mport structure */ 815296077Sadrian mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT); 816296077Sadrian if (mport == NULL) { 817296077Sadrian error = ENOMEM; 818296077Sadrian goto failed; 819296077Sadrian } 820296077Sadrian 821296077Sadrian mport->mp_vid = mpd.port_vid; 822296077Sadrian mport->mp_num = mpd.port_num; 823296077Sadrian 824296077Sadrian /* Update dinfo */ 825296077Sadrian STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link); 826296077Sadrian } 827296077Sadrian 828296077Sadrian 829296077Sadrian /* 830296077Sadrian * Determine whether this is a bridge device; if so, we can 831296077Sadrian * expect the first sequence of address region descriptors to 832296077Sadrian * be of EROM_REGION_TYPE_BRIDGE instead of 833296077Sadrian * BCMA_EROM_REGION_TYPE_DEVICE. 834296077Sadrian * 835296077Sadrian * It's unclear whether this is the correct mechanism by which we 836296077Sadrian * should detect/handle bridge devices, but this approach matches 837296077Sadrian * that of (some of) Broadcom's published drivers. 838296077Sadrian */ 839296077Sadrian if (core.num_dport > 0) { 840296077Sadrian uint32_t entry; 841296077Sadrian 842296077Sadrian if ((error = bcma_erom_peek32(erom, &entry))) 843296077Sadrian goto failed; 844296077Sadrian 845296077Sadrian if (BCMA_EROM_ENTRY_IS(entry, REGION) && 846296077Sadrian BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE) 847296077Sadrian { 848296077Sadrian first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE; 849296077Sadrian cfg->num_dev_ports = 0; 850296077Sadrian cfg->num_bridge_ports = core.num_dport; 851296077Sadrian } else { 852296077Sadrian first_region_type = BCMA_EROM_REGION_TYPE_DEVICE; 853296077Sadrian cfg->num_dev_ports = core.num_dport; 854296077Sadrian cfg->num_bridge_ports = 0; 855296077Sadrian } 856296077Sadrian } 857296077Sadrian 858296077Sadrian /* Device/bridge port descriptors */ 859296077Sadrian for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) { 860296077Sadrian error = erom_corecfg_fill_port_regions(erom, cfg, sp_num, 861296077Sadrian first_region_type); 862296077Sadrian 863296077Sadrian if (error) 864296077Sadrian goto failed; 865296077Sadrian } 866296077Sadrian 867296077Sadrian /* Wrapper (aka device management) descriptors (for master ports). */ 868296077Sadrian for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) { 869296077Sadrian error = erom_corecfg_fill_port_regions(erom, cfg, sp_num, 870296077Sadrian BCMA_EROM_REGION_TYPE_MWRAP); 871296077Sadrian 872296077Sadrian if (error) 873296077Sadrian goto failed; 874296077Sadrian } 875296077Sadrian 876296077Sadrian 877296077Sadrian /* Wrapper (aka device management) descriptors (for slave ports). */ 878296077Sadrian for (uint8_t i = 0; i < core.num_swrap; i++) { 879296077Sadrian /* Slave wrapper ports are not numbered distinctly from master 880296077Sadrian * wrapper ports. */ 881299314Sadrian 882299314Sadrian /* 883299314Sadrian * Broadcom DDR1/DDR2 Memory Controller 884299314Sadrian * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) -> 885299314Sadrian * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2) 886299314Sadrian * 887299314Sadrian * ARM BP135 AMBA3 AXI to APB Bridge 888299314Sadrian * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) -> 889299314Sadrian * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2) 890299314Sadrian * 891299314Sadrian * core.num_mwrap 892299314Sadrian * ===> 893299314Sadrian * (core.num_mwrap > 0) ? 894299314Sadrian * core.num_mwrap : 895299314Sadrian * ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) 896299314Sadrian */ 897299314Sadrian uint8_t sp_num; 898299314Sadrian sp_num = (core.num_mwrap > 0) ? 899299314Sadrian core.num_mwrap : 900299314Sadrian ((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i; 901296077Sadrian error = erom_corecfg_fill_port_regions(erom, cfg, sp_num, 902296077Sadrian BCMA_EROM_REGION_TYPE_SWRAP); 903296077Sadrian 904296077Sadrian if (error) 905296077Sadrian goto failed; 906296077Sadrian } 907296077Sadrian 908296077Sadrian *result = cfg; 909296077Sadrian return (0); 910296077Sadrian 911296077Sadrianfailed: 912296077Sadrian if (cfg != NULL) 913296077Sadrian bcma_free_corecfg(cfg); 914296077Sadrian 915296077Sadrian return error; 916296077Sadrian} 917