/*-1* Copyright (c) 2015-2016 Landon Fuller <[email protected]>2* Copyright (c) 2017 The FreeBSD Foundation3* All rights reserved.4*5* Portions of this software were developed by Landon Fuller6* under sponsorship from the FreeBSD Foundation.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer,13* without modification.14* 2. Redistributions in binary form must reproduce at minimum a disclaimer15* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any16* redistribution must be conditioned upon including a substantially17* similar Disclaimer requirement for further binary redistribution.18*19* NO WARRANTY20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS21* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT22* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY23* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL24* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,25* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER28* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF30* THE POSSIBILITY OF SUCH DAMAGES.31*/3233#include <sys/param.h>34#include <sys/bus.h>35#include <sys/kernel.h>36#include <sys/limits.h>37#include <sys/systm.h>3839#include <machine/bus.h>40#include <machine/resource.h>4142#include <dev/bhnd/bhndvar.h>4344#include "sibareg.h"45#include "sibavar.h"4647static int siba_register_interrupts(device_t dev, device_t child,48struct siba_devinfo *dinfo);49static int siba_append_dinfo_region(struct siba_devinfo *dinfo,50uint8_t addridx, uint32_t base, uint32_t size,51uint32_t bus_reserved);5253/**54* Map a siba(4) OCP vendor code to its corresponding JEDEC JEP-106 vendor55* code.56*57* @param ocp_vendor An OCP vendor code.58* @return The BHND_MFGID constant corresponding to @p ocp_vendor, or59* BHND_MFGID_INVALID if the OCP vendor is unknown.60*/61uint16_t62siba_get_bhnd_mfgid(uint16_t ocp_vendor)63{64switch (ocp_vendor) {65case OCP_VENDOR_BCM:66return (BHND_MFGID_BCM);67default:68return (BHND_MFGID_INVALID);69}70}7172/**73* Allocate and return a new empty device info structure.74*75* @param bus The requesting bus device.76*77* @retval NULL if allocation failed.78*/79struct siba_devinfo *80siba_alloc_dinfo(device_t bus)81{82struct siba_devinfo *dinfo;8384dinfo = malloc(sizeof(struct siba_devinfo), M_BHND, M_NOWAIT|M_ZERO);85if (dinfo == NULL)86return NULL;8788for (u_int i = 0; i < nitems(dinfo->cfg); i++) {89dinfo->cfg[i] = ((struct siba_cfg_block){90.cb_base = 0,91.cb_size = 0,92.cb_rid = -1,93});94dinfo->cfg_res[i] = NULL;95}9697resource_list_init(&dinfo->resources);9899dinfo->pmu_state = SIBA_PMU_NONE;100101dinfo->intr = (struct siba_intr) {102.mapped = false,103.rid = -1104};105106return dinfo;107}108109/**110* Initialize a device info structure previously allocated via111* siba_alloc_dinfo, copying the provided core id.112*113* @param dev The requesting bus device.114* @param child The siba child device.115* @param dinfo The device info instance.116* @param core Device core info.117*118* @retval 0 success119* @retval non-zero initialization failed.120*/121int122siba_init_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo,123const struct siba_core_id *core_id)124{125int error;126127dinfo->core_id = *core_id;128129/* Register all address space mappings */130for (uint8_t i = 0; i < core_id->num_admatch; i++) {131uint32_t bus_reserved;132133/* If this is the device's core/enumeration addrespace,134* reserve the Sonics configuration register blocks for the135* use of our bus. */136bus_reserved = 0;137if (i == SIBA_CORE_ADDRSPACE)138bus_reserved = core_id->num_cfg_blocks * SIBA_CFG_SIZE;139140/* Append the region info */141error = siba_append_dinfo_region(dinfo, i,142core_id->admatch[i].am_base, core_id->admatch[i].am_size,143bus_reserved);144if (error)145return (error);146}147148/* Register all interrupt(s) */149if ((error = siba_register_interrupts(dev, child, dinfo)))150return (error);151152return (0);153}154155/**156* Register and map all interrupts for @p dinfo.157*158* @param dev The siba bus device.159* @param child The siba child device.160* @param dinfo The device info instance on which to register all interrupt161* entries.162*/163static int164siba_register_interrupts(device_t dev, device_t child,165struct siba_devinfo *dinfo)166{167int error;168169/* Is backplane interrupt distribution enabled for this core? */170if (!dinfo->core_id.intr_en)171return (0);172173/* Have one interrupt */174dinfo->intr.mapped = false;175dinfo->intr.irq = 0;176dinfo->intr.rid = -1;177178/* Map the interrupt */179error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,180&dinfo->intr.irq);181if (error) {182device_printf(dev, "failed mapping interrupt line for core %u: "183"%d\n", dinfo->core_id.core_info.core_idx, error);184return (error);185}186dinfo->intr.mapped = true;187188/* Update the resource list */189dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,190dinfo->intr.irq, dinfo->intr.irq, 1);191192return (0);193}194195/**196* Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port197* number.198*199* @param addrspace Address space index.200*/201u_int202siba_addrspace_device_port(u_int addrspace)203{204/* The first addrspace is always mapped to device0; the remainder205* are mapped to device1 */206if (addrspace == 0)207return (0);208else209return (1);210}211212/**213* Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port214* region number.215*216* @param addrspace Address space index.217*/218u_int219siba_addrspace_device_region(u_int addrspace)220{221/* The first addrspace is always mapped to device0.0; the remainder222* are mapped to device1.0 + (n - 1) */223if (addrspace == 0)224return (0);225else226return (addrspace - 1);227}228229/**230* Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port231* number.232*233* @param cfg Config block index.234*/235u_int236siba_cfg_agent_port(u_int cfg)237{238/* Always agent0 */239return (0);240}241242/**243* Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port244* region number.245*246* @param cfg Config block index.247*/248u_int249siba_cfg_agent_region(u_int cfg)250{251/* Always agent0.<idx> */252return (cfg);253}254255/**256* Return the number of bhnd(4) ports to advertise for the given257* @p core_id and @p port_type.258*259* Refer to the siba_addrspace_index() and siba_cfg_index() functions for260* information on siba's mapping of bhnd(4) port and region identifiers.261*262* @param core_id The siba core info.263* @param port_type The bhnd(4) port type.264*/265u_int266siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)267{268switch (port_type) {269case BHND_PORT_DEVICE:270/* 0, 1, or 2 ports */271return (min(core_id->num_admatch, 2));272273case BHND_PORT_AGENT:274/* One agent port maps all configuration blocks */275if (core_id->num_cfg_blocks > 0)276return (1);277278/* Do not advertise an agent port if there are no configuration279* register blocks */280return (0);281282default:283return (0);284}285}286287/**288* Return true if @p port of @p port_type is defined by @p core_id, false289* otherwise.290*291* @param core_id The siba core info.292* @param port_type The bhnd(4) port type.293* @param port The bhnd(4) port number.294*/295bool296siba_is_port_valid(struct siba_core_id *core_id, bhnd_port_type port_type,297u_int port)298{299/* Verify the index against the port count */300if (siba_port_count(core_id, port_type) <= port)301return (false);302303return (true);304}305306/**307* Return the number of bhnd(4) regions to advertise for @p core_id on the308* @p port of @p port_type.309*310* @param core_id The siba core info.311* @param port_type The bhnd(4) port type.312*/313u_int314siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,315u_int port)316{317/* The port must exist */318if (!siba_is_port_valid(core_id, port_type, port))319return (0);320321switch (port_type) {322case BHND_PORT_DEVICE:323/* The first address space, if any, is mapped to device0.0 */324if (port == 0)325return (min(core_id->num_admatch, 1));326327/* All remaining address spaces are mapped to device0.(n - 1) */328if (port == 1 && core_id->num_admatch >= 2)329return (core_id->num_admatch - 1);330331break;332333case BHND_PORT_AGENT:334/* All config blocks are mapped to a single port */335if (port == 0)336return (core_id->num_cfg_blocks);337338break;339340default:341break;342}343344/* Validated above */345panic("siba_is_port_valid() returned true for unknown %s.%u port",346bhnd_port_type_name(port_type), port);347348}349350/**351* Map a bhnd(4) type/port/region triplet to its associated config block index,352* if any.353*354* We map config registers to port/region identifiers as follows:355*356* [port].[region] [cfg register block]357* agent0.0 0358* agent0.1 1359*360* @param port_type The bhnd(4) port type.361* @param port The bhnd(4) port number.362* @param region The bhnd(4) port region.363* @param addridx On success, the corresponding addrspace index.364*365* @retval 0 success366* @retval ENOENT if the given type/port/region cannot be mapped to a367* siba config register block.368*/369int370siba_cfg_index(struct siba_core_id *core_id, bhnd_port_type port_type,371u_int port, u_int region, u_int *cfgidx)372{373/* Config blocks are mapped to agent ports */374if (port_type != BHND_PORT_AGENT)375return (ENOENT);376377/* Port must be valid */378if (!siba_is_port_valid(core_id, port_type, port))379return (ENOENT);380381if (region >= core_id->num_cfg_blocks)382return (ENOENT);383384if (region >= SIBA_MAX_CFG)385return (ENOENT);386387/* Found */388*cfgidx = region;389return (0);390}391392/**393* Map an bhnd(4) type/port/region triplet to its associated config block394* entry, if any.395*396* The only supported port type is BHND_PORT_DEVICE.397*398* @param dinfo The device info to search for a matching address space.399* @param type The bhnd(4) port type.400* @param port The bhnd(4) port number.401* @param region The bhnd(4) port region.402*/403struct siba_cfg_block *404siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,405u_int region)406{407u_int cfgidx;408int error;409410/* Map to addrspace index */411error = siba_cfg_index(&dinfo->core_id, type, port, region, &cfgidx);412if (error)413return (NULL);414415/* Found */416return (&dinfo->cfg[cfgidx]);417}418419/**420* Map a bhnd(4) type/port/region triplet to its associated address space421* index, if any.422*423* For compatibility with bcma(4), we map address spaces to port/region424* identifiers as follows:425*426* [port.region] [admatch index]427* device0.0 0428* device1.0 1429* device1.1 2430* device1.2 3431*432* @param core_id The siba core info.433* @param port_type The bhnd(4) port type.434* @param port The bhnd(4) port number.435* @param region The bhnd(4) port region.436* @param addridx On success, the corresponding addrspace index.437*438* @retval 0 success439* @retval ENOENT if the given type/port/region cannot be mapped to a440* siba address space.441*/442int443siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,444u_int port, u_int region, u_int *addridx)445{446u_int idx;447448/* Address spaces are always device ports */449if (port_type != BHND_PORT_DEVICE)450return (ENOENT);451452/* Port must be valid */453if (!siba_is_port_valid(core_id, port_type, port))454return (ENOENT);455456if (port == 0)457idx = region;458else if (port == 1)459idx = region + 1;460else461return (ENOENT);462463if (idx >= core_id->num_admatch)464return (ENOENT);465466/* Found */467*addridx = idx;468return (0);469}470471/**472* Map an bhnd(4) type/port/region triplet to its associated address space473* entry, if any.474*475* The only supported port type is BHND_PORT_DEVICE.476*477* @param dinfo The device info to search for a matching address space.478* @param type The bhnd(4) port type.479* @param port The bhnd(4) port number.480* @param region The bhnd(4) port region.481*/482struct siba_addrspace *483siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,484u_int region)485{486u_int addridx;487int error;488489/* Map to addrspace index */490error = siba_addrspace_index(&dinfo->core_id, type, port, region,491&addridx);492if (error)493return (NULL);494495/* Found */496if (addridx >= SIBA_MAX_ADDRSPACE)497return (NULL);498499return (&dinfo->addrspace[addridx]);500}501502/**503* Append an address space entry to @p dinfo.504*505* @param dinfo The device info entry to update.506* @param addridx The address space index.507* @param base The mapping's base address.508* @param size The mapping size.509* @param bus_reserved Number of bytes to reserve in @p size for bus use510* when registering the resource list entry. This is used to reserve bus511* access to the core's SIBA_CFG* register blocks.512*513* @retval 0 success514* @retval non-zero An error occurred appending the entry.515*/516static int517siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,518uint32_t base, uint32_t size, uint32_t bus_reserved)519{520struct siba_addrspace *sa;521rman_res_t r_size;522523/* Verify that base + size will not overflow */524if (size > 0 && UINT32_MAX - (size - 1) < base)525return (ERANGE);526527/* Verify that size - bus_reserved will not underflow */528if (size < bus_reserved)529return (ERANGE);530531/* Must not be 0-length */532if (size == 0)533return (EINVAL);534535/* Must not exceed addrspace array size */536if (addridx >= nitems(dinfo->addrspace))537return (EINVAL);538539/* Initialize new addrspace entry */540sa = &dinfo->addrspace[addridx];541sa->sa_base = base;542sa->sa_size = size;543sa->sa_bus_reserved = bus_reserved;544545/* Populate the resource list */546r_size = size - bus_reserved;547sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,548base, base + (r_size - 1), r_size);549550return (0);551}552553/**554* Deallocate the given device info structure and any associated resources.555*556* @param dev The requesting bus device.557* @param child The siba child device.558* @param dinfo Device info associated with @p child to be deallocated.559*/560void561siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)562{563resource_list_free(&dinfo->resources);564565/* Free all mapped configuration blocks */566for (u_int i = 0; i < nitems(dinfo->cfg); i++) {567if (dinfo->cfg_res[i] == NULL)568continue;569570bhnd_release_resource(dev, dinfo->cfg_res[i]);571572dinfo->cfg_res[i] = NULL;573}574575/* Unmap the core's interrupt */576if (dinfo->core_id.intr_en && dinfo->intr.mapped) {577BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);578dinfo->intr.mapped = false;579}580581free(dinfo, M_BHND);582}583584/**585* Return the core-enumeration-relative offset for the @p addrspace586* SIBA_R0_ADMATCH* register.587*588* @param addrspace The address space index.589*590* @retval non-zero success591* @retval 0 the given @p addrspace index is not supported.592*/593u_int594siba_admatch_offset(uint8_t addrspace)595{596switch (addrspace) {597case 0:598return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);599case 1:600return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);601case 2:602return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);603case 3:604return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);605default:606return (0);607}608}609610/**611* Parse a SIBA_R0_ADMATCH* register.612*613* @param addrspace The address space index.614* @param am The address match register value to be parsed.615* @param[out] admatch The parsed address match descriptor616*617* @retval 0 success618* @retval non-zero a parse error occurred.619*/620int621siba_parse_admatch(uint32_t am, struct siba_admatch *admatch)622{623u_int am_type;624625/* Extract the base address and size */626am_type = SIBA_REG_GET(am, AM_TYPE);627switch (am_type) {628case 0:629/* Type 0 entries are always enabled, and do not support630* negative matching */631admatch->am_base = am & SIBA_AM_BASE0_MASK;632admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);633admatch->am_enabled = true;634admatch->am_negative = false;635break;636case 1:637admatch->am_base = am & SIBA_AM_BASE1_MASK;638admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);639admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);640admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);641break;642case 2:643admatch->am_base = am & SIBA_AM_BASE2_MASK;644admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);645admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);646admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);647break;648default:649return (EINVAL);650}651652return (0);653}654655/**656* Write @p value to @p dev's CFG0 target/initiator state register, performing657* required read-back and waiting for completion.658*659* @param dev The siba(4) child device.660* @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW,661* SIBA_CFG0_IMSTATE)662* @param value The value to write to @p reg.663* @param mask The mask of bits to be included from @p value.664*/665void666siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,667bus_size_t reg, uint32_t value, uint32_t mask)668{669struct bhnd_resource *r;670uint32_t rval;671672r = dinfo->cfg_res[0];673674KASSERT(r != NULL, ("%s missing CFG0 mapping",675device_get_nameunit(dev)));676KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx",677device_get_nameunit(dev), (uintmax_t)reg));678679rval = bhnd_bus_read_4(r, reg);680rval &= ~mask;681rval |= (value & mask);682683bhnd_bus_write_4(r, reg, rval);684bhnd_bus_read_4(r, reg); /* read-back */685DELAY(1);686}687688/**689* Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state690* register value to be equal to @p value after applying @p mask bits to both691* values.692*693* @param dev The siba(4) child device to wait on.694* @param dinfo The @p dev's device info695* @param reg The state register to read (e.g. SIBA_CFG0_TMSTATEHIGH,696* SIBA_CFG0_IMSTATE)697* @param value The value against which @p reg will be compared.698* @param mask The mask to be applied when comparing @p value with @p reg.699* @param usec The maximum number of microseconds to wait for completion.700*701* @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.702* @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.703* @retval ETIMEDOUT if a timeout occurs.704*/705int706siba_wait_target_state(device_t dev, struct siba_devinfo *dinfo, bus_size_t reg,707uint32_t value, uint32_t mask, u_int usec)708{709struct bhnd_resource *r;710uint32_t rval;711712if ((r = dinfo->cfg_res[0]) == NULL)713return (ENODEV);714715value &= mask;716for (int i = 0; i < usec; i += 10) {717rval = bhnd_bus_read_4(r, reg);718if ((rval & mask) == value)719return (0);720721DELAY(10);722}723724return (ETIMEDOUT);725}726727728