/*-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;95dinfo->cfg_rid[i] = -1;96}9798resource_list_init(&dinfo->resources);99100dinfo->pmu_state = SIBA_PMU_NONE;101102dinfo->intr = (struct siba_intr) {103.mapped = false,104.rid = -1105};106107return dinfo;108}109110/**111* Initialize a device info structure previously allocated via112* siba_alloc_dinfo, copying the provided core id.113*114* @param dev The requesting bus device.115* @param child The siba child device.116* @param dinfo The device info instance.117* @param core Device core info.118*119* @retval 0 success120* @retval non-zero initialization failed.121*/122int123siba_init_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo,124const struct siba_core_id *core_id)125{126int error;127128dinfo->core_id = *core_id;129130/* Register all address space mappings */131for (uint8_t i = 0; i < core_id->num_admatch; i++) {132uint32_t bus_reserved;133134/* If this is the device's core/enumeration addrespace,135* reserve the Sonics configuration register blocks for the136* use of our bus. */137bus_reserved = 0;138if (i == SIBA_CORE_ADDRSPACE)139bus_reserved = core_id->num_cfg_blocks * SIBA_CFG_SIZE;140141/* Append the region info */142error = siba_append_dinfo_region(dinfo, i,143core_id->admatch[i].am_base, core_id->admatch[i].am_size,144bus_reserved);145if (error)146return (error);147}148149/* Register all interrupt(s) */150if ((error = siba_register_interrupts(dev, child, dinfo)))151return (error);152153return (0);154}155156/**157* Register and map all interrupts for @p dinfo.158*159* @param dev The siba bus device.160* @param child The siba child device.161* @param dinfo The device info instance on which to register all interrupt162* entries.163*/164static int165siba_register_interrupts(device_t dev, device_t child,166struct siba_devinfo *dinfo)167{168int error;169170/* Is backplane interrupt distribution enabled for this core? */171if (!dinfo->core_id.intr_en)172return (0);173174/* Have one interrupt */175dinfo->intr.mapped = false;176dinfo->intr.irq = 0;177dinfo->intr.rid = -1;178179/* Map the interrupt */180error = BHND_BUS_MAP_INTR(dev, child, 0 /* single intr is always 0 */,181&dinfo->intr.irq);182if (error) {183device_printf(dev, "failed mapping interrupt line for core %u: "184"%d\n", dinfo->core_id.core_info.core_idx, error);185return (error);186}187dinfo->intr.mapped = true;188189/* Update the resource list */190dinfo->intr.rid = resource_list_add_next(&dinfo->resources, SYS_RES_IRQ,191dinfo->intr.irq, dinfo->intr.irq, 1);192193return (0);194}195196/**197* Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port198* number.199*200* @param addrspace Address space index.201*/202u_int203siba_addrspace_device_port(u_int addrspace)204{205/* The first addrspace is always mapped to device0; the remainder206* are mapped to device1 */207if (addrspace == 0)208return (0);209else210return (1);211}212213/**214* Map an addrspace index to its corresponding bhnd(4) BHND_PORT_DEVICE port215* region number.216*217* @param addrspace Address space index.218*/219u_int220siba_addrspace_device_region(u_int addrspace)221{222/* The first addrspace is always mapped to device0.0; the remainder223* are mapped to device1.0 + (n - 1) */224if (addrspace == 0)225return (0);226else227return (addrspace - 1);228}229230/**231* Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port232* number.233*234* @param cfg Config block index.235*/236u_int237siba_cfg_agent_port(u_int cfg)238{239/* Always agent0 */240return (0);241}242243/**244* Map an config block index to its corresponding bhnd(4) BHND_PORT_AGENT port245* region number.246*247* @param cfg Config block index.248*/249u_int250siba_cfg_agent_region(u_int cfg)251{252/* Always agent0.<idx> */253return (cfg);254}255256/**257* Return the number of bhnd(4) ports to advertise for the given258* @p core_id and @p port_type.259*260* Refer to the siba_addrspace_index() and siba_cfg_index() functions for261* information on siba's mapping of bhnd(4) port and region identifiers.262*263* @param core_id The siba core info.264* @param port_type The bhnd(4) port type.265*/266u_int267siba_port_count(struct siba_core_id *core_id, bhnd_port_type port_type)268{269switch (port_type) {270case BHND_PORT_DEVICE:271/* 0, 1, or 2 ports */272return (min(core_id->num_admatch, 2));273274case BHND_PORT_AGENT:275/* One agent port maps all configuration blocks */276if (core_id->num_cfg_blocks > 0)277return (1);278279/* Do not advertise an agent port if there are no configuration280* register blocks */281return (0);282283default:284return (0);285}286}287288/**289* Return true if @p port of @p port_type is defined by @p core_id, false290* otherwise.291*292* @param core_id The siba core info.293* @param port_type The bhnd(4) port type.294* @param port The bhnd(4) port number.295*/296bool297siba_is_port_valid(struct siba_core_id *core_id, bhnd_port_type port_type,298u_int port)299{300/* Verify the index against the port count */301if (siba_port_count(core_id, port_type) <= port)302return (false);303304return (true);305}306307/**308* Return the number of bhnd(4) regions to advertise for @p core_id on the309* @p port of @p port_type.310*311* @param core_id The siba core info.312* @param port_type The bhnd(4) port type.313*/314u_int315siba_port_region_count(struct siba_core_id *core_id, bhnd_port_type port_type,316u_int port)317{318/* The port must exist */319if (!siba_is_port_valid(core_id, port_type, port))320return (0);321322switch (port_type) {323case BHND_PORT_DEVICE:324/* The first address space, if any, is mapped to device0.0 */325if (port == 0)326return (min(core_id->num_admatch, 1));327328/* All remaining address spaces are mapped to device0.(n - 1) */329if (port == 1 && core_id->num_admatch >= 2)330return (core_id->num_admatch - 1);331332break;333334case BHND_PORT_AGENT:335/* All config blocks are mapped to a single port */336if (port == 0)337return (core_id->num_cfg_blocks);338339break;340341default:342break;343}344345/* Validated above */346panic("siba_is_port_valid() returned true for unknown %s.%u port",347bhnd_port_type_name(port_type), port);348349}350351/**352* Map a bhnd(4) type/port/region triplet to its associated config block index,353* if any.354*355* We map config registers to port/region identifiers as follows:356*357* [port].[region] [cfg register block]358* agent0.0 0359* agent0.1 1360*361* @param port_type The bhnd(4) port type.362* @param port The bhnd(4) port number.363* @param region The bhnd(4) port region.364* @param addridx On success, the corresponding addrspace index.365*366* @retval 0 success367* @retval ENOENT if the given type/port/region cannot be mapped to a368* siba config register block.369*/370int371siba_cfg_index(struct siba_core_id *core_id, bhnd_port_type port_type,372u_int port, u_int region, u_int *cfgidx)373{374/* Config blocks are mapped to agent ports */375if (port_type != BHND_PORT_AGENT)376return (ENOENT);377378/* Port must be valid */379if (!siba_is_port_valid(core_id, port_type, port))380return (ENOENT);381382if (region >= core_id->num_cfg_blocks)383return (ENOENT);384385if (region >= SIBA_MAX_CFG)386return (ENOENT);387388/* Found */389*cfgidx = region;390return (0);391}392393/**394* Map an bhnd(4) type/port/region triplet to its associated config block395* entry, if any.396*397* The only supported port type is BHND_PORT_DEVICE.398*399* @param dinfo The device info to search for a matching address space.400* @param type The bhnd(4) port type.401* @param port The bhnd(4) port number.402* @param region The bhnd(4) port region.403*/404struct siba_cfg_block *405siba_find_cfg_block(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,406u_int region)407{408u_int cfgidx;409int error;410411/* Map to addrspace index */412error = siba_cfg_index(&dinfo->core_id, type, port, region, &cfgidx);413if (error)414return (NULL);415416/* Found */417return (&dinfo->cfg[cfgidx]);418}419420/**421* Map a bhnd(4) type/port/region triplet to its associated address space422* index, if any.423*424* For compatibility with bcma(4), we map address spaces to port/region425* identifiers as follows:426*427* [port.region] [admatch index]428* device0.0 0429* device1.0 1430* device1.1 2431* device1.2 3432*433* @param core_id The siba core info.434* @param port_type The bhnd(4) port type.435* @param port The bhnd(4) port number.436* @param region The bhnd(4) port region.437* @param addridx On success, the corresponding addrspace index.438*439* @retval 0 success440* @retval ENOENT if the given type/port/region cannot be mapped to a441* siba address space.442*/443int444siba_addrspace_index(struct siba_core_id *core_id, bhnd_port_type port_type,445u_int port, u_int region, u_int *addridx)446{447u_int idx;448449/* Address spaces are always device ports */450if (port_type != BHND_PORT_DEVICE)451return (ENOENT);452453/* Port must be valid */454if (!siba_is_port_valid(core_id, port_type, port))455return (ENOENT);456457if (port == 0)458idx = region;459else if (port == 1)460idx = region + 1;461else462return (ENOENT);463464if (idx >= core_id->num_admatch)465return (ENOENT);466467/* Found */468*addridx = idx;469return (0);470}471472/**473* Map an bhnd(4) type/port/region triplet to its associated address space474* entry, if any.475*476* The only supported port type is BHND_PORT_DEVICE.477*478* @param dinfo The device info to search for a matching address space.479* @param type The bhnd(4) port type.480* @param port The bhnd(4) port number.481* @param region The bhnd(4) port region.482*/483struct siba_addrspace *484siba_find_addrspace(struct siba_devinfo *dinfo, bhnd_port_type type, u_int port,485u_int region)486{487u_int addridx;488int error;489490/* Map to addrspace index */491error = siba_addrspace_index(&dinfo->core_id, type, port, region,492&addridx);493if (error)494return (NULL);495496/* Found */497if (addridx >= SIBA_MAX_ADDRSPACE)498return (NULL);499500return (&dinfo->addrspace[addridx]);501}502503/**504* Append an address space entry to @p dinfo.505*506* @param dinfo The device info entry to update.507* @param addridx The address space index.508* @param base The mapping's base address.509* @param size The mapping size.510* @param bus_reserved Number of bytes to reserve in @p size for bus use511* when registering the resource list entry. This is used to reserve bus512* access to the core's SIBA_CFG* register blocks.513*514* @retval 0 success515* @retval non-zero An error occurred appending the entry.516*/517static int518siba_append_dinfo_region(struct siba_devinfo *dinfo, uint8_t addridx,519uint32_t base, uint32_t size, uint32_t bus_reserved)520{521struct siba_addrspace *sa;522rman_res_t r_size;523524/* Verify that base + size will not overflow */525if (size > 0 && UINT32_MAX - (size - 1) < base)526return (ERANGE);527528/* Verify that size - bus_reserved will not underflow */529if (size < bus_reserved)530return (ERANGE);531532/* Must not be 0-length */533if (size == 0)534return (EINVAL);535536/* Must not exceed addrspace array size */537if (addridx >= nitems(dinfo->addrspace))538return (EINVAL);539540/* Initialize new addrspace entry */541sa = &dinfo->addrspace[addridx];542sa->sa_base = base;543sa->sa_size = size;544sa->sa_bus_reserved = bus_reserved;545546/* Populate the resource list */547r_size = size - bus_reserved;548sa->sa_rid = resource_list_add_next(&dinfo->resources, SYS_RES_MEMORY,549base, base + (r_size - 1), r_size);550551return (0);552}553554/**555* Deallocate the given device info structure and any associated resources.556*557* @param dev The requesting bus device.558* @param child The siba child device.559* @param dinfo Device info associated with @p child to be deallocated.560*/561void562siba_free_dinfo(device_t dev, device_t child, struct siba_devinfo *dinfo)563{564resource_list_free(&dinfo->resources);565566/* Free all mapped configuration blocks */567for (u_int i = 0; i < nitems(dinfo->cfg); i++) {568if (dinfo->cfg_res[i] == NULL)569continue;570571bhnd_release_resource(dev, SYS_RES_MEMORY, dinfo->cfg_rid[i],572dinfo->cfg_res[i]);573574dinfo->cfg_res[i] = NULL;575dinfo->cfg_rid[i] = -1;576}577578/* Unmap the core's interrupt */579if (dinfo->core_id.intr_en && dinfo->intr.mapped) {580BHND_BUS_UNMAP_INTR(dev, child, dinfo->intr.irq);581dinfo->intr.mapped = false;582}583584free(dinfo, M_BHND);585}586587/**588* Return the core-enumeration-relative offset for the @p addrspace589* SIBA_R0_ADMATCH* register.590*591* @param addrspace The address space index.592*593* @retval non-zero success594* @retval 0 the given @p addrspace index is not supported.595*/596u_int597siba_admatch_offset(uint8_t addrspace)598{599switch (addrspace) {600case 0:601return SB0_REG_ABS(SIBA_CFG0_ADMATCH0);602case 1:603return SB0_REG_ABS(SIBA_CFG0_ADMATCH1);604case 2:605return SB0_REG_ABS(SIBA_CFG0_ADMATCH2);606case 3:607return SB0_REG_ABS(SIBA_CFG0_ADMATCH3);608default:609return (0);610}611}612613/**614* Parse a SIBA_R0_ADMATCH* register.615*616* @param addrspace The address space index.617* @param am The address match register value to be parsed.618* @param[out] admatch The parsed address match descriptor619*620* @retval 0 success621* @retval non-zero a parse error occurred.622*/623int624siba_parse_admatch(uint32_t am, struct siba_admatch *admatch)625{626u_int am_type;627628/* Extract the base address and size */629am_type = SIBA_REG_GET(am, AM_TYPE);630switch (am_type) {631case 0:632/* Type 0 entries are always enabled, and do not support633* negative matching */634admatch->am_base = am & SIBA_AM_BASE0_MASK;635admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT0) + 1);636admatch->am_enabled = true;637admatch->am_negative = false;638break;639case 1:640admatch->am_base = am & SIBA_AM_BASE1_MASK;641admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT1) + 1);642admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);643admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);644break;645case 2:646admatch->am_base = am & SIBA_AM_BASE2_MASK;647admatch->am_size = 1 << (SIBA_REG_GET(am, AM_ADINT2) + 1);648admatch->am_enabled = ((am & SIBA_AM_ADEN) != 0);649admatch->am_negative = ((am & SIBA_AM_ADNEG) != 0);650break;651default:652return (EINVAL);653}654655return (0);656}657658/**659* Write @p value to @p dev's CFG0 target/initiator state register, performing660* required read-back and waiting for completion.661*662* @param dev The siba(4) child device.663* @param reg The CFG0 state register to write (e.g. SIBA_CFG0_TMSTATELOW,664* SIBA_CFG0_IMSTATE)665* @param value The value to write to @p reg.666* @param mask The mask of bits to be included from @p value.667*/668void669siba_write_target_state(device_t dev, struct siba_devinfo *dinfo,670bus_size_t reg, uint32_t value, uint32_t mask)671{672struct bhnd_resource *r;673uint32_t rval;674675r = dinfo->cfg_res[0];676677KASSERT(r != NULL, ("%s missing CFG0 mapping",678device_get_nameunit(dev)));679KASSERT(reg <= SIBA_CFG_SIZE-4, ("%s invalid CFG0 register offset %#jx",680device_get_nameunit(dev), (uintmax_t)reg));681682rval = bhnd_bus_read_4(r, reg);683rval &= ~mask;684rval |= (value & mask);685686bhnd_bus_write_4(r, reg, rval);687bhnd_bus_read_4(r, reg); /* read-back */688DELAY(1);689}690691/**692* Spin for up to @p usec waiting for @p dev's CFG0 target/initiator state693* register value to be equal to @p value after applying @p mask bits to both694* values.695*696* @param dev The siba(4) child device to wait on.697* @param dinfo The @p dev's device info698* @param reg The state register to read (e.g. SIBA_CFG0_TMSTATEHIGH,699* SIBA_CFG0_IMSTATE)700* @param value The value against which @p reg will be compared.701* @param mask The mask to be applied when comparing @p value with @p reg.702* @param usec The maximum number of microseconds to wait for completion.703*704* @retval 0 if SIBA_TMH_BUSY is cleared prior to the @p usec timeout.705* @retval ENODEV if SIBA_CFG0 is not mapped by @p dinfo.706* @retval ETIMEDOUT if a timeout occurs.707*/708int709siba_wait_target_state(device_t dev, struct siba_devinfo *dinfo, bus_size_t reg,710uint32_t value, uint32_t mask, u_int usec)711{712struct bhnd_resource *r;713uint32_t rval;714715if ((r = dinfo->cfg_res[0]) == NULL)716return (ENODEV);717718value &= mask;719for (int i = 0; i < usec; i += 10) {720rval = bhnd_bus_read_4(r, reg);721if ((rval & mask) == value)722return (0);723724DELAY(10);725}726727return (ETIMEDOUT);728}729730731