/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2016 Landon Fuller <[email protected]>4* Copyright (c) 2017 The FreeBSD Foundation5* All rights reserved.6*7* Portions of this software were developed by Landon Fuller8* under sponsorship from the FreeBSD Foundation.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer,15* without modification.16* 2. Redistributions in binary form must reproduce at minimum a disclaimer17* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any18* redistribution must be conditioned upon including a substantially19* similar Disclaimer requirement for further binary redistribution.20*21* NO WARRANTY22* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS23* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT24* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY25* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL26* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,27* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF28* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS29* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER30* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)31* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF32* THE POSSIBILITY OF SUCH DAMAGES.33*/3435#include <sys/param.h>36#include <sys/bus.h>37#include <sys/kobj.h>3839#include <machine/bus.h>40#include <sys/rman.h>41#include <machine/resource.h>4243#include <dev/bhnd/bhndreg.h>44#include <dev/bhnd/bhndvar.h>4546#include <dev/bhnd/bhnd_erom.h>47#include <dev/bhnd/bhnd_eromvar.h>4849#include <dev/bhnd/cores/chipc/chipcreg.h>5051static int bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,52bhnd_size_t size);53static int bhnd_erom_iores_tell(struct bhnd_erom_io *eio,54bhnd_addr_t *addr, bhnd_size_t *size);55static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio,56bhnd_size_t offset, u_int width);57static void bhnd_erom_iores_fini(struct bhnd_erom_io *eio);5859static int bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,60bhnd_size_t size);61static int bhnd_erom_iobus_tell(struct bhnd_erom_io *eio,62bhnd_addr_t *addr, bhnd_size_t *size);63static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,64bhnd_size_t offset, u_int width);6566/**67* An implementation of bhnd_erom_io that manages mappings via68* bhnd_alloc_resource() and bhnd_release_resource().69*/70struct bhnd_erom_iores {71struct bhnd_erom_io eio;72device_t owner; /**< device from which we'll allocate resources */73int owner_rid; /**< rid to use when allocating new mappings */74struct bhnd_resource *mapped; /**< current mapping, or NULL */75int mapped_rid; /**< resource ID of current mapping, or -1 */76};7778/**79* Fetch the device enumeration parser class from all bhnd(4)-compatible drivers80* registered for @p bus_devclass, probe @p eio for supporting parser classes,81* and return the best available supporting enumeration parser class.82*83* @param bus_devclass The bus device class to be queried for84* bhnd(4)-compatible drivers.85* @param eio An erom bus I/O instance, configured with a86* mapping of the first bus core.87* @param hint Identification hint used to identify the device.88* If the chipset supports standard chip89* identification registers within the first core,90* this parameter should be NULL.91* @param[out] cid On success, the probed chip identifier.92*93* @retval non-NULL on success, the best available EROM class.94* @retval NULL if no erom class returned a successful probe result for95* @p eio.96*/97bhnd_erom_class_t *98bhnd_erom_probe_driver_classes(devclass_t bus_devclass,99struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,100struct bhnd_chipid *cid)101{102driver_t **drivers;103int drv_count;104bhnd_erom_class_t *erom_cls;105int error, prio, result;106107erom_cls = NULL;108prio = 0;109110/* Fetch all available drivers */111error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);112if (error) {113printf("error fetching bhnd(4) drivers for %s: %d\n",114devclass_get_name(bus_devclass), error);115return (NULL);116}117118/* Enumerate the drivers looking for the best available EROM class */119for (int i = 0; i < drv_count; i++) {120struct bhnd_chipid pcid;121bhnd_erom_class_t *cls;122123/* The default implementation of BHND_BUS_GET_EROM_CLASS()124* returns NULL if unimplemented; this should always be safe125* to call on arbitrary drivers */126cls = bhnd_driver_get_erom_class(drivers[i]);127if (cls == NULL)128continue;129130kobj_class_compile(cls);131132/* Probe the bus */133result = bhnd_erom_probe(cls, eio, hint, &pcid);134135/* The parser did not match if an error was returned */136if (result > 0)137continue;138139/* Check for a new highest priority match */140if (erom_cls == NULL || result > prio) {141prio = result;142143*cid = pcid;144erom_cls = cls;145}146147/* Terminate immediately on BUS_PROBE_SPECIFIC */148if (result == BUS_PROBE_SPECIFIC)149break;150}151152free(drivers, M_TEMP);153return (erom_cls);154}155156/**157* Allocate and return a new device enumeration table parser.158*159* @param cls The parser class for which an instance will be160* allocated.161* @param eio The bus I/O callbacks to use when reading the device162* enumeration table.163* @param cid The device's chip identifier.164*165* @retval non-NULL success166* @retval NULL if an error occurred allocating or initializing the167* EROM parser.168*/169bhnd_erom_t *170bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,171struct bhnd_erom_io *eio)172{173bhnd_erom_t *erom;174int error;175176erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,177M_WAITOK|M_ZERO);178179if ((error = BHND_EROM_INIT(erom, cid, eio))) {180printf("error initializing %s parser at %#jx: %d\n", cls->name,181(uintmax_t)cid->enum_addr, error);182183kobj_delete((kobj_t)erom, M_BHND);184return (NULL);185}186187return (erom);188}189190/**191* Perform static initialization of a device enumeration table parser.192*193* This may be used to initialize a caller-allocated erom instance state194* during early boot, prior to malloc availability.195*196* @param cls The parser class for which an instance will be197* allocated.198* @param erom The erom parser instance to initialize.199* @param esize The total available number of bytes allocated for200* @p erom. If this is less than is required by @p cls,201* ENOMEM will be returned.202* @param cid The device's chip identifier.203* @param eio The bus I/O callbacks to use when reading the device204* enumeration table.205*206* @retval 0 success207* @retval ENOMEM if @p esize is smaller than required by @p cls.208* @retval non-zero if an error occurs initializing the EROM parser,209* a regular unix error code will be returned.210*/211int212bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,213const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)214{215kobj_class_t kcls;216217kcls = (kobj_class_t)cls;218219/* Verify allocation size */220if (kcls->size > esize)221return (ENOMEM);222223/* Perform instance initialization */224kobj_init_static((kobj_t)erom, kcls);225return (BHND_EROM_INIT(erom, cid, eio));226}227228/**229* Release any resources held by a @p erom parser previously230* initialized via bhnd_erom_init_static().231*232* @param erom An erom parser instance previously initialized via233* bhnd_erom_init_static().234*/235void236bhnd_erom_fini_static(bhnd_erom_t *erom)237{238return (BHND_EROM_FINI(erom));239}240241/**242* Release all resources held by a @p erom parser previously243* allocated via bhnd_erom_alloc().244*245* @param erom An erom parser instance previously allocated via246* bhnd_erom_alloc().247*/248void249bhnd_erom_free(bhnd_erom_t *erom)250{251BHND_EROM_FINI(erom);252kobj_delete((kobj_t)erom, M_BHND);253}254255/**256* Read the chip identification registers mapped by @p eio, popuating @p cid257* with the parsed result258*259* @param eio A bus I/O instance, configured with a mapping260* of the ChipCommon core.261* @param[out] cid On success, the parsed chip identification.262*263* @warning264* On early siba(4) devices, the ChipCommon core does not provide265* a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions266* (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return267* an invalid `ncores` value.268*/269int270bhnd_erom_read_chipid(struct bhnd_erom_io *eio, struct bhnd_chipid *cid)271{272bhnd_addr_t cc_addr;273bhnd_size_t cc_size;274uint32_t idreg, cc_caps;275int error;276277/* Fetch ChipCommon address */278if ((error = bhnd_erom_io_tell(eio, &cc_addr, &cc_size)))279return (error);280281/* Read chip identifier */282idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);283284/* Extract the basic chip info */285cid->chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP);286cid->chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG);287cid->chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV);288cid->chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);289cid->ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE);290291/* Populate EROM address */292if (BHND_CHIPTYPE_HAS_EROM(cid->chip_type)) {293cid->enum_addr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);294} else {295cid->enum_addr = cc_addr;296}297298/* Populate capability flags */299cc_caps = bhnd_erom_io_read(eio, CHIPC_CAPABILITIES, 4);300cid->chip_caps = 0x0;301302if (cc_caps & CHIPC_CAP_BKPLN64)303cid->chip_caps |= BHND_CAP_BP64;304305if (cc_caps & CHIPC_CAP_PMU)306cid->chip_caps |= BHND_CAP_PMU;307308return (0);309}310311/**312* Attempt to map @p size bytes at @p addr, replacing any existing313* @p eio mapping.314*315* @param eio I/O instance state.316* @param addr The address to be mapped.317* @param size The number of bytes to be mapped at @p addr.318*319* @retval 0 success320* @retval non-zero if mapping @p addr otherwise fails, a regular321* unix error code should be returned.322*/323int324bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)325{326return (eio->map(eio, addr, size));327}328329/**330* Return the address range mapped by @p eio, if any.331*332* @param eio I/O instance state.333* @param[out] addr The address mapped by @p eio.334* @param[out] size The number of bytes mapped at @p addr.335*336* @retval 0 success337* @retval ENXIO if @p eio has no mapping.338*/339int340bhnd_erom_io_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,341bhnd_size_t *size)342{343return (eio->tell(eio, addr, size));344}345346/**347* Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset348* relative to @p eio's current mapping.349*350* @param eio erom I/O callbacks351* @param offset read offset.352* @param width item width (1, 2, or 4 bytes).353*/354uint32_t355bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)356{357return (eio->read(eio, offset, width));358}359360/**361* Free all resources held by @p eio.362*/363void364bhnd_erom_io_fini(struct bhnd_erom_io *eio)365{366if (eio->fini != NULL)367return (eio->fini(eio));368}369370/**371* Allocate, initialize, and return a new I/O instance that will perform372* mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.373*374* @param dev The device to pass to bhnd_alloc_resource() and375* bhnd_release_resource() functions.376* @param rid The resource ID to be used when allocating memory resources.377*/378struct bhnd_erom_io *379bhnd_erom_iores_new(device_t dev, int rid)380{381struct bhnd_erom_iores *iores;382383iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);384iores->eio.map = bhnd_erom_iores_map;385iores->eio.tell = bhnd_erom_iores_tell;386iores->eio.read = bhnd_erom_iores_read;387iores->eio.fini = bhnd_erom_iores_fini;388389iores->owner = dev;390iores->owner_rid = rid;391iores->mapped = NULL;392iores->mapped_rid = -1;393394return (&iores->eio);395}396397static int398bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,399bhnd_size_t size)400{401struct bhnd_erom_iores *iores;402403iores = (struct bhnd_erom_iores *)eio;404405/* Sanity check the addr/size */406if (size == 0)407return (EINVAL);408409if (BHND_ADDR_MAX - size < addr)410return (EINVAL); /* would overflow */411412/* Check for an existing mapping */413if (iores->mapped) {414/* If already mapped, nothing else to do */415if (rman_get_start(iores->mapped->res) == addr &&416rman_get_size(iores->mapped->res) == size)417{418return (0);419}420421/* Otherwise, we need to drop the existing mapping */422bhnd_release_resource(iores->owner, SYS_RES_MEMORY,423iores->mapped_rid, iores->mapped);424iores->mapped = NULL;425iores->mapped_rid = -1;426}427428/* Try to allocate the new mapping */429iores->mapped_rid = iores->owner_rid;430iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,431&iores->mapped_rid, addr, addr+size-1, size,432RF_ACTIVE|RF_SHAREABLE);433if (iores->mapped == NULL) {434iores->mapped_rid = -1;435return (ENXIO);436}437438return (0);439}440441static int442bhnd_erom_iores_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,443bhnd_size_t *size)444{445struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;446447if (iores->mapped == NULL)448return (ENXIO);449450*addr = rman_get_start(iores->mapped->res);451*size = rman_get_size(iores->mapped->res);452453return (0);454}455456static uint32_t457bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)458{459struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;460461if (iores->mapped == NULL)462panic("read with invalid mapping");463464switch (width) {465case 1:466return (bhnd_bus_read_1(iores->mapped, offset));467case 2:468return (bhnd_bus_read_2(iores->mapped, offset));469case 4:470return (bhnd_bus_read_4(iores->mapped, offset));471default:472panic("invalid width %u", width);473}474}475476static void477bhnd_erom_iores_fini(struct bhnd_erom_io *eio)478{479struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;480481/* Release any mapping */482if (iores->mapped) {483bhnd_release_resource(iores->owner, SYS_RES_MEMORY,484iores->mapped_rid, iores->mapped);485iores->mapped = NULL;486iores->mapped_rid = -1;487}488489free(eio, M_BHND);490}491492/**493* Initialize an I/O instance that will perform mapping directly from the494* given bus space tag and handle.495*496* @param iobus The I/O instance to be initialized.497* @param addr The base address mapped by @p bsh.498* @param size The total size mapped by @p bsh.499* @param bst Bus space tag for @p bsh.500* @param bsh Bus space handle mapping the full bus enumeration space.501*502* @retval 0 success503* @retval non-zero if initializing @p iobus otherwise fails, a regular504* unix error code will be returned.505*/506int507bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,508bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)509{510iobus->eio.map = bhnd_erom_iobus_map;511iobus->eio.tell = bhnd_erom_iobus_tell;512iobus->eio.read = bhnd_erom_iobus_read;513iobus->eio.fini = NULL;514515iobus->addr = addr;516iobus->size = size;517iobus->bst = bst;518iobus->bsh = bsh;519iobus->mapped = false;520521return (0);522}523524static int525bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,526bhnd_size_t size)527{528struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;529530/* Sanity check the addr/size */531if (size == 0)532return (EINVAL);533534/* addr+size must not overflow */535if (BHND_ADDR_MAX - size < addr)536return (EINVAL);537538/* addr/size must fit within our bus tag's mapping */539if (addr < iobus->addr || size > iobus->size)540return (ENXIO);541542if (iobus->size - (addr - iobus->addr) < size)543return (ENXIO);544545/* The new addr offset and size must be representible as a bus_size_t */546if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)547return (ENXIO);548549if (size > BUS_SPACE_MAXSIZE)550return (ENXIO);551552iobus->offset = addr - iobus->addr;553iobus->limit = size;554iobus->mapped = true;555556return (0);557}558559static int560bhnd_erom_iobus_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,561bhnd_size_t *size)562{563struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;564565if (!iobus->mapped)566return (ENXIO);567568*addr = iobus->addr + iobus->offset;569*size = iobus->limit;570571return (0);572}573574static uint32_t575bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)576{577struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;578579if (!iobus->mapped)580panic("no active mapping");581582if (iobus->limit < width || iobus->limit - width < offset)583panic("invalid offset %#jx", offset);584585switch (width) {586case 1:587return (bus_space_read_1(iobus->bst, iobus->bsh,588iobus->offset + offset));589case 2:590return (bus_space_read_2(iobus->bst, iobus->bsh,591iobus->offset + offset));592case 4:593return (bus_space_read_4(iobus->bst, iobus->bsh,594iobus->offset + offset));595default:596panic("invalid width %u", width);597}598}599600601