From owner-svn-src-all@freebsd.org Tue Nov 28 00:12:16 2017 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 52DA7DEDE6F; Tue, 28 Nov 2017 00:12:16 +0000 (UTC) (envelope-from landonf@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 03AF66B66A; Tue, 28 Nov 2017 00:12:15 +0000 (UTC) (envelope-from landonf@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id vAS0CFOW051259; Tue, 28 Nov 2017 00:12:15 GMT (envelope-from landonf@FreeBSD.org) Received: (from landonf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id vAS0CEjO051249; Tue, 28 Nov 2017 00:12:14 GMT (envelope-from landonf@FreeBSD.org) Message-Id: <201711280012.vAS0CEjO051249@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: landonf set sender to landonf@FreeBSD.org using -f From: "Landon J. Fuller" Date: Tue, 28 Nov 2017 00:12:14 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r326297 - head/sys/dev/bhnd/bhndb X-SVN-Group: head X-SVN-Commit-Author: landonf X-SVN-Commit-Paths: head/sys/dev/bhnd/bhndb X-SVN-Commit-Revision: 326297 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 28 Nov 2017 00:12:16 -0000 Author: landonf Date: Tue Nov 28 00:12:14 2017 New Revision: 326297 URL: https://svnweb.freebsd.org/changeset/base/326297 Log: bhndb(4): Implement bridge support for the BCM4312 and other PCI_V0 chipsets. Very early (PCI_V0) Broadcom PCI Wi-Fi chipsets have a few quirks when compared to later PCI(e) core revisions: - The standard static BAR0 mapping of the PCI core registers is discontiguous, with siba's cfg0 register block mapped distinctly from the other core registers. - No dedicated ChipCommon register mapping is provided; instead, the single configurable register window must be used to access both ChipCommon and D11 core registers. The D11 core's operational semantics guarantee the safety of -- after disabling interrupts -- borrowing the single dynamic register window to perform the few ChipCommon operations required by a driver. To support these early PCI devices: - Allow defining multiple discontiguous BHNDB_REGWIN_T_CORE register windows that map a single port/region, and producing bridged resource allocations backed by those discontiguous windows. - Support stealing existing register window allocations to fulfill indirect bhnd(4) bus I/O requests within address ranges tagged with BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT. - Fix an inverted test of bhndb_is_pcie_attached() that disabled PCI-only clock bring-up required by these devices. Approved by: adrian (mentor, implicit) Sponsored by: The FreeBSD Foundation Modified: head/sys/dev/bhnd/bhndb/bhndb.c head/sys/dev/bhnd/bhndb/bhndb.h head/sys/dev/bhnd/bhndb/bhndb_hwdata.c head/sys/dev/bhnd/bhndb/bhndb_pci.c head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c head/sys/dev/bhnd/bhndb/bhndb_pcireg.h head/sys/dev/bhnd/bhndb/bhndb_private.h head/sys/dev/bhnd/bhndb/bhndb_subr.c head/sys/dev/bhnd/bhndb/bhndbvar.h Modified: head/sys/dev/bhnd/bhndb/bhndb.c ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.c Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb.c Tue Nov 28 00:12:14 2017 (r326297) @@ -115,9 +115,9 @@ static int bhndb_try_activate_resource( static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, - bus_size_t *offset); + bus_size_t *offset, bool *stolen, + bus_addr_t *restore); - /** * Default bhndb(4) implementation of DEVICE_PROBE(). * @@ -270,6 +270,9 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero for (regw = br->cfg->register_windows; regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) { + const struct bhndb_port_priority *pp; + uint32_t alloc_flags; + /* Only core windows are supported */ if (regw->win_type != BHNDB_REGWIN_T_CORE) continue; @@ -295,6 +298,18 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero } /* + * Apply the register window's region offset, if any. + */ + if (regw->d.core.offset > size) { + device_printf(sc->dev, "invalid register " + "window offset %#jx for region %#jx+%#jx\n", + regw->d.core.offset, addr, size); + return (EINVAL); + } + + addr += regw->d.core.offset; + + /* * Always defer to the register window's size. * * If the port size is smaller than the window size, @@ -307,14 +322,25 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero */ size = regw->win_size; + /* Fetch allocation flags from the corresponding port + * priority entry, if any */ + pp = bhndb_hw_priorty_find_port(table, core, + regw->d.core.port_type, regw->d.core.port, + regw->d.core.region); + if (pp != NULL) { + alloc_flags = pp->alloc_flags; + } else { + alloc_flags = 0; + } + /* * Add to the bus region list. * - * The window priority for a statically mapped - * region is always HIGH. + * The window priority for a statically mapped region is + * always HIGH. */ error = bhndb_add_resource_region(br, addr, size, - BHNDB_PRIORITY_HIGH, regw); + BHNDB_PRIORITY_HIGH, 0, regw); if (error) return (error); } @@ -325,7 +351,6 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero * ports defined in the priority table */ for (u_int i = 0; i < ncores; i++) { - struct bhndb_region *region; struct bhnd_core_info *core; struct bhnd_core_match md; @@ -369,13 +394,12 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero } /* Skip ports with an existing static mapping */ - region = bhndb_find_resource_region(br, addr, size); - if (region != NULL && region->static_regwin != NULL) + if (bhndb_has_static_region_mapping(br, addr, size)) continue; /* Define a dynamic region for this port */ error = bhndb_add_resource_region(br, addr, size, - pp->priority, NULL); + pp->priority, pp->alloc_flags, NULL); if (error) return (error); @@ -416,22 +440,29 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero struct bhndb_region *region; const char *direct_msg, *type_msg; bhndb_priority_t prio, prio_min; + uint32_t flags; prio_min = br->min_prio; device_printf(sc->dev, "min_prio: %d\n", prio_min); STAILQ_FOREACH(region, &br->bus_regions, link) { prio = region->priority; + flags = region->alloc_flags; direct_msg = prio >= prio_min ? "direct" : "indirect"; type_msg = region->static_regwin ? "static" : "dynamic"; device_printf(sc->dev, "region 0x%llx+0x%llx priority " - "%u %s/%s\n", + "%u %s/%s", (unsigned long long) region->addr, (unsigned long long) region->size, region->priority, direct_msg, type_msg); + + if (flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) + printf(" [overcommit]\n"); + else + printf("\n"); } } @@ -1605,11 +1636,12 @@ bhndb_deactivate_bhnd_resource(device_t dev, device_t * in-use region; the first matching region is returned. */ static struct bhndb_dw_alloc * -bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, - bus_size_t size, bus_size_t *offset) +bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, + bus_size_t *offset, bool *stolen, bus_addr_t *restore) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; + struct bhndb_region *region; BHNDB_LOCK_ASSERT(sc, MA_OWNED); @@ -1638,10 +1670,25 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add *offset = dwa->win->win_offset; *offset += addr - dwa->target; + *stolen = false; return (dwa); } - /* not found */ + /* No existing dynamic mapping found. We'll need to check for a defined + * region to determine whether we can fulfill this request by + * stealing from an existing allocated register window */ + region = bhndb_find_resource_region(br, addr, size); + if (region == NULL) + return (NULL); + + if ((region->alloc_flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) == 0) + return (NULL); + + if ((dwa = bhndb_dw_steal(br, restore)) != NULL) { + *stolen = true; + return (dwa); + } + return (NULL); } @@ -1649,9 +1696,8 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add * Return a borrowed reference to a bridge resource allocation record capable * of handling bus I/O requests of @p size at @p addr. * - * This will either return a reference to an existing allocation - * record mapping the requested space, or will configure and return a free - * allocation record. + * This will either return a reference to an existing allocation record mapping + * the requested space, or will configure and return a free allocation record. * * Will panic if a usable record cannot be found. * @@ -1660,10 +1706,16 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add * @param size The size of the I/O operation to be performed at @p addr. * @param[out] offset The offset within the returned resource at which * to perform the I/O request. + * @param[out] stolen Set to true if the allocation record was stolen to fulfill + * this request. If a stolen allocation record is returned, + * bhndb_io_resource_restore() must be called upon completion of the bus I/O + * request. + * @param[out] restore If the allocation record was stolen, this will be set + * to the target that must be restored. */ static inline struct bhndb_dw_alloc * bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, - bus_size_t *offset) + bus_size_t *offset, bool *stolen, bus_addr_t *restore) { struct bhndb_resources *br; struct bhndb_dw_alloc *dwa; @@ -1691,7 +1743,8 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a * current operation. */ if (dwa == NULL) { - dwa = bhndb_io_resource_slow(sc, addr, size, offset); + dwa = bhndb_io_resource_slow(sc, addr, size, offset, stolen, + restore); if (dwa == NULL) { panic("register windows exhausted attempting to map " "0x%llx-0x%llx\n", @@ -1720,6 +1773,7 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a /* Calculate the offset and return */ *offset = (addr - dwa->target) + dwa->win->win_offset; + *stolen = false; return (dwa); } @@ -1733,12 +1787,14 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a struct bhndb_dw_alloc *dwa; \ struct resource *io_res; \ bus_size_t io_offset; \ + bus_addr_t restore; \ + bool stolen; \ \ sc = device_get_softc(dev); \ \ BHNDB_LOCK(sc); \ dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ - offset, _io_size, &io_offset); \ + offset, _io_size, &io_offset, &stolen, &restore); \ io_res = dwa->parent_res; \ \ KASSERT(!r->direct, \ @@ -1748,6 +1804,10 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a ("i/o resource is not active")); #define BHNDB_IO_COMMON_TEARDOWN() \ + if (stolen) { \ + bhndb_dw_return_stolen(sc->dev, sc->bus_res, \ + dwa, restore); \ + } \ BHNDB_UNLOCK(sc); /* Defines a bhndb_bus_read_* method implementation */ Modified: head/sys/dev/bhnd/bhndb/bhndb.h ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb.h Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb.h Tue Nov 28 00:12:14 2017 (r326297) @@ -90,6 +90,7 @@ struct bhndb_regwin { bhnd_port_type port_type; /**< mapped port type */ u_int port; /**< mapped port number */ u_int region; /**< mapped region number */ + bhnd_size_t offset; /**< mapped offset within the region */ } core; /** SPROM register window (BHNDB_REGWIN_T_SPROM). */ @@ -150,6 +151,26 @@ typedef enum { } bhndb_priority_t; /** + * bhndb resource allocation flags. + */ +enum bhndb_alloc_flags { + /** + * If resource overcommit prevents fulfilling a request for this + * resource, an in-use resource should be be borrowed to fulfill the + * request. + * + * The only known use case is to support accessing the ChipCommon core + * during Wi-Fi driver operation on early PCI Wi-Fi devices + * (PCI_V0, SSB) that do not provide a dedicated ChipCommon register + * window mapping; on such devices, device and firmware semantics + * guarantee the safety of -- after disabling interrupts -- borrowing + * the single dynamic register window that's been assigned to the D11 + * core to perform the few ChipCommon operations required by the driver. + */ + BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT = (1<<0), +}; + +/** * Port resource priority descriptor. */ struct bhndb_port_priority { @@ -157,6 +178,7 @@ struct bhndb_port_priority { u_int port; /**< port */ u_int region; /**< region */ bhndb_priority_t priority; /**< port priority */ + uint32_t alloc_flags; /**< port allocation flags (@see bhndb_alloc_flags) */ }; /** Modified: head/sys/dev/bhnd/bhndb/bhndb_hwdata.c ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_hwdata.c Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb_hwdata.c Tue Nov 28 00:12:14 2017 (r326297) @@ -76,19 +76,24 @@ __FBSDID("$FreeBSD$"); BHNDB_PORTS(__VA_ARGS__) \ } -/* Define a port priority record for the type/port/region - * triplet. */ -#define BHNDB_PORT_PRIO(_type, _port, _region, _priority) { \ +/* Define a port priority record for the type/port/region triplet, optionally + * specifying port allocation flags as the final argument */ +#define BHNDB_PORT_PRIO(_type, _port, _region, _priority, ...) \ + _BHNDB_PORT_PRIO(_type, _port, _region, _priority, ## __VA_ARGS__, 0) + +#define _BHNDB_PORT_PRIO(_type, _port, _region, _priority, _flags, ...) \ +{ \ .type = (BHND_PORT_ ## _type), \ .port = _port, \ .region = _region, \ - .priority = (BHNDB_PRIORITY_ ## _priority) \ + .priority = (BHNDB_PRIORITY_ ## _priority), \ + .alloc_flags = (_flags) \ } /* Define a port priority record for the default (_type, 0, 0) type/port/region * triplet. */ -#define BHNDB_PORT0_PRIO(_type, _priority) \ - BHNDB_PORT_PRIO(_type, 0, 0, _priority) +#define BHNDB_PORT0_PRIO(_type, _priority, ...) \ + BHNDB_PORT_PRIO(_type, 0, 0, _priority, ## __VA_ARGS__, 0) /** * Generic resource priority configuration usable with all currently supported @@ -170,10 +175,14 @@ const struct bhndb_hw_priority bhndb_siba_priority_tab * Agent ports are marked as 'NONE' on siba(4) devices, as they * will be fully mappable via register windows shared with the * device0.0 port. + * + * To support early PCI_V0 devices, we enable FULFILL_ON_OVERCOMMIT for + * ChipCommon. */ BHNDB_CLASS_PRIO(CC, LOW, /* Device Block */ - BHNDB_PORT_PRIO(DEVICE, 0, 0, LOW) + BHNDB_PORT_PRIO(DEVICE, 0, 0, LOW, + BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) ), BHNDB_CLASS_PRIO(PMU, LOW, @@ -193,4 +202,4 @@ const struct bhndb_hw_priority bhndb_siba_priority_tab ), BHNDB_HW_PRIORITY_TABLE_END -}; \ No newline at end of file +}; Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci.c Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb_pci.c Tue Nov 28 00:12:14 2017 (r326297) @@ -707,26 +707,28 @@ bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) * Return the host resource providing a static mapping of the PCI core's * registers. * - * @param sc bhndb PCI driver state. - * @param[out] res On success, the host resource containing our PCI - * core's register window. - * @param[out] offset On success, the offset of the PCI core registers within - * @p res. + * @param sc bhndb PCI driver state. + * @param offset The required readable offset within the PCI core + * register block. + * @param size The required readable size at @p offset. + * @param[out] res On success, the host resource containing our PCI + * core's register window. + * @param[out] res_offset On success, the @p offset relative to @p res. * * @retval 0 success * @retval ENXIO if a valid static register window mapping the PCI core * registers is not available. */ static int -bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, struct resource **res, - bus_size_t *offset) +bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, bus_size_t offset, + bus_size_t size, struct resource **res, bus_size_t *res_offset) { const struct bhndb_regwin *win; struct resource *r; - /* Locate the static register window mapping the PCI core */ + /* Locate the static register window mapping the requested offset */ win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows, - sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0); + sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size); if (win == NULL) { device_printf(sc->dev, "missing PCI core register window\n"); return (ENXIO); @@ -739,8 +741,11 @@ bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, st return (ENXIO); } + KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of " + "register window", (uintmax_t)offset)); + *res = r; - *offset = win->win_offset; + *res_offset = win->win_offset + (offset - win->d.core.offset); return (0); } @@ -761,18 +766,21 @@ bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_s bus_size_t r_offset; int error; - if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset))) - panic("no PCI core registers: %d", error); + error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset); + if (error) { + panic("no PCI register window mapping %#jx+%#x: %d", + (uintmax_t)offset, width, error); + } switch (width) { case 1: - bus_write_1(r, r_offset + offset, value); + bus_write_1(r, r_offset, value); break; case 2: - bus_write_2(r, r_offset + offset, value); + bus_write_2(r, r_offset, value); break; case 4: - bus_write_4(r, r_offset + offset, value); + bus_write_4(r, r_offset, value); break; default: panic("invalid width: %u", width); @@ -794,16 +802,19 @@ bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_si bus_size_t r_offset; int error; - if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset))) - panic("no PCI core registers: %d", error); + error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset); + if (error) { + panic("no PCI register window mapping %#jx+%#x: %d", + (uintmax_t)offset, width, error); + } switch (width) { case 1: - return (bus_read_1(r, r_offset + offset)); + return (bus_read_1(r, r_offset)); case 2: - return (bus_read_2(r, r_offset + offset)); + return (bus_read_2(r, r_offset)); case 4: - return (bus_read_4(r, r_offset + offset)); + return (bus_read_4(r, r_offset)); default: panic("invalid width: %u", width); } @@ -1057,7 +1068,7 @@ bhndb_enable_pci_clocks(device_t dev) pci_dev = device_get_parent(dev); /* Only supported and required on PCI devices */ - if (!bhndb_is_pcie_attached(dev)) + if (bhndb_is_pcie_attached(dev)) return (0); /* Read state of XTAL pin */ Modified: head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c Tue Nov 28 00:12:14 2017 (r326297) @@ -312,7 +312,13 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = { .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, - /* bar0+0x1800: pci core registers */ + /* + * bar0+0x1800: pci core registers. + * + * Does not include the SSB CFG registers found at the end of + * the 4K core register block; these are mapped non-contigiously + * by the next entry. + */ { .win_type = BHNDB_REGWIN_T_CORE, .win_offset = BHNDB_PCI_V0_BAR0_PCIREG_OFFSET, @@ -322,10 +328,27 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = { .unit = 0, .port = 0, .region = 0, + .port_type = BHND_PORT_DEVICE, + }, + .res = { SYS_RES_MEMORY, PCIR_BAR(0) } + }, + + /* bar0+0x1E00: pci core (SSB CFG registers) */ + { + .win_type = BHNDB_REGWIN_T_CORE, + .win_offset = BHNDB_PCI_V0_BAR0_PCISB_OFFSET , + .win_size = BHNDB_PCI_V0_BAR0_PCISB_SIZE, + .d.core = { + .class = BHND_DEVCLASS_PCI, + .unit = 0, + .port = 0, + .region = 0, + .offset = BHNDB_PCI_V0_BAR0_PCISB_COREOFF, .port_type = BHND_PORT_DEVICE }, .res = { SYS_RES_MEMORY, PCIR_BAR(0) } }, + BHNDB_REGWIN_TABLE_END }, Modified: head/sys/dev/bhnd/bhndb/bhndb_pcireg.h ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pcireg.h Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb_pcireg.h Tue Nov 28 00:12:14 2017 (r326297) @@ -40,7 +40,8 @@ * [offset+ size] type description * [0x0000+0x1000] dynamic mapped backplane address space (window 0). * [0x1000+0x0800] fixed SPROM shadow - * [0x1800+0x0800] fixed pci core registers + * [0x1800+0x0E00] fixed pci core device registers + * [0x1E00+0x0200] fixed pci core siba config registers * * == PCI_V1 == * Applies to: @@ -133,8 +134,11 @@ #define BHNDB_PCI_V0_BAR0_WIN0_SIZE 0x1000 #define BHNDB_PCI_V0_BAR0_SPROM_OFFSET 0x1000 /* bar0 + 4K accesses sprom shadow (in pci core) */ #define BHNDB_PCI_V0_BAR0_SPROM_SIZE 0x0800 -#define BHNDB_PCI_V0_BAR0_PCIREG_OFFSET 0x1800 /* bar0 + 6K accesses pci core registers */ -#define BHNDB_PCI_V0_BAR0_PCIREG_SIZE 0x0800 +#define BHNDB_PCI_V0_BAR0_PCIREG_OFFSET 0x1800 /* bar0 + 6K accesses pci core registers (not including SSB CFG registers) */ +#define BHNDB_PCI_V0_BAR0_PCIREG_SIZE 0x0E00 +#define BHNDB_PCI_V0_BAR0_PCISB_OFFSET 0x1E00 /* bar0 + 7.5K accesses pci core's SSB CFG register blocks */ +#define BHNDB_PCI_V0_BAR0_PCISB_SIZE 0x0200 +#define BHNDB_PCI_V0_BAR0_PCISB_COREOFF 0xE00 /* mapped offset relative to the core base address */ /* PCI_V1 */ #define BHNDB_PCI_V1_BAR0_WIN0_CONTROL 0x80 /* backplane address space accessed by BAR0/WIN0 */ Modified: head/sys/dev/bhnd/bhndb/bhndb_private.h ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_private.h Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb_private.h Tue Nov 28 00:12:14 2017 (r326297) @@ -70,6 +70,7 @@ int bhndb_add_resource_region( struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size, bhndb_priority_t priority, + uint32_t alloc_flags, const struct bhndb_regwin *static_regwin); int bhndb_find_resource_limits( @@ -93,6 +94,10 @@ struct bhndb_intr_handler *bhndb_find_intr_handler( struct bhndb_resources *br, void *cookiep); +bool bhndb_has_static_region_mapping( + struct bhndb_resources *br, + bhnd_addr_t addr, bhnd_size_t size); + struct bhndb_region *bhndb_find_resource_region( struct bhndb_resources *br, bhnd_addr_t addr, bhnd_size_t size); @@ -120,11 +125,25 @@ int bhndb_dw_set_addr(device_t dev, struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size); +struct bhndb_dw_alloc *bhndb_dw_steal(struct bhndb_resources *br, + bus_addr_t *saved); + +void bhndb_dw_return_stolen(device_t dev, + struct bhndb_resources *br, + struct bhndb_dw_alloc *dwa, + bus_addr_t saved); + const struct bhndb_hw_priority *bhndb_hw_priority_find_core( const struct bhndb_hw_priority *table, struct bhnd_core_info *core); +const struct bhndb_port_priority *bhndb_hw_priorty_find_port( + const struct bhndb_hw_priority *table, + struct bhnd_core_info *core, + bhnd_port_type port_type, u_int port, + u_int region); + /** * Dynamic register window allocation reference. */ @@ -152,6 +171,7 @@ struct bhndb_region { bhnd_addr_t addr; /**< start of mapped range */ bhnd_size_t size; /**< size of mapped range */ bhndb_priority_t priority; /**< direct resource allocation priority */ + uint32_t alloc_flags; /**< resource allocation flags (@see bhndb_alloc_flags) */ const struct bhndb_regwin *static_regwin; /**< fixed mapping regwin, if any */ STAILQ_ENTRY(bhndb_region) link; @@ -185,6 +205,7 @@ struct bhndb_resources { STAILQ_HEAD(, bhndb_region) bus_regions; /**< bus region descriptors */ + struct mtx dw_steal_mtx; /**< spinlock must be held when stealing a dynamic window allocation */ struct bhndb_dw_alloc *dw_alloc; /**< dynamic window allocation records */ size_t dwa_count; /**< number of dynamic windows available. */ bitstr_t *dwa_freelist; /**< dynamic window free list */ Modified: head/sys/dev/bhnd/bhndb/bhndb_subr.c ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_subr.c Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndb_subr.c Tue Nov 28 00:12:14 2017 (r326297) @@ -291,7 +291,10 @@ bhndb_alloc_resources(device_t dev, device_t parent_de r->min_prio = BHNDB_PRIORITY_NONE; STAILQ_INIT(&r->bus_regions); STAILQ_INIT(&r->bus_intrs); - + + mtx_init(&r->dw_steal_mtx, device_get_nameunit(dev), + "bhndb dwa_steal lock", MTX_SPIN); + /* Initialize host address space resource manager. */ r->ht_mem_rman.rm_start = 0; r->ht_mem_rman.rm_end = ~0; @@ -492,6 +495,8 @@ failed: if (r->res != NULL) bhndb_release_host_resources(r->res); + mtx_destroy(&r->dw_steal_mtx); + free(r, M_BHND); return (NULL); @@ -626,6 +631,10 @@ bhndb_free_resources(struct bhndb_resources *br) free(br->dw_alloc, M_BHND); free(br->dwa_freelist, M_BHND); + + mtx_destroy(&br->dw_steal_mtx); + + free(br, M_BHND); } /** @@ -1054,6 +1063,7 @@ bhndb_find_resource_limits(struct bhndb_resources *br, * @param size The size of this region. * @param priority The resource priority to be assigned to allocations * made within this bus region. + * @param alloc_flags resource allocation flags (@see bhndb_alloc_flags) * @param static_regwin If available, a static register window mapping this * bus region entry. If not available, NULL. * @@ -1062,7 +1072,7 @@ bhndb_find_resource_limits(struct bhndb_resources *br, */ int bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, - bhnd_size_t size, bhndb_priority_t priority, + bhnd_size_t size, bhndb_priority_t priority, uint32_t alloc_flags, const struct bhndb_regwin *static_regwin) { struct bhndb_region *reg; @@ -1076,6 +1086,7 @@ bhndb_add_resource_region(struct bhndb_resources *br, .addr = addr, .size = size, .priority = priority, + .alloc_flags = alloc_flags, .static_regwin = static_regwin }; @@ -1084,7 +1095,40 @@ bhndb_add_resource_region(struct bhndb_resources *br, return (0); } +/** + * Return true if a mapping of @p size bytes at @p addr is provided by either + * one contiguous bus region, or by multiple discontiguous regions. + * + * @param br The resource state to query. + * @param addr The requested starting address. + * @param size The requested size. + */ +bool +bhndb_has_static_region_mapping(struct bhndb_resources *br, + bhnd_addr_t addr, bhnd_size_t size) +{ + struct bhndb_region *region; + bhnd_addr_t r_addr; + r_addr = addr; + while ((region = bhndb_find_resource_region(br, r_addr, 1)) != NULL) { + /* Must be backed by a static register window */ + if (region->static_regwin == NULL) + return (false); + + /* Adjust the search offset */ + r_addr += region->size; + + /* Have we traversed a complete (if discontiguous) mapping? */ + if (r_addr == addr + size) + return (true); + + } + + /* No complete mapping found */ + return (false); +} + /** * Find the bus region that maps @p size bytes at @p addr. * @@ -1302,7 +1346,7 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources rw = dwa->win; - KASSERT(bhndb_dw_is_free(br, dwa), + KASSERT(bhndb_dw_is_free(br, dwa) || mtx_owned(&br->dw_steal_mtx), ("attempting to set the target address on an in-use window")); /* Page-align the target address */ @@ -1324,6 +1368,74 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources } /** + * Steal an in-use allocation record from @p br, returning the record's current + * target in @p saved on success. + * + * This function acquires a mutex and disables interrupts; callers should + * avoid holding a stolen window longer than required to issue an I/O + * request. + * + * A successful call to bhndb_dw_steal() must be balanced with a call to + * bhndb_dw_return_stolen(). + * + * @param br The resource state from which a window should be stolen. + * @param saved The stolen window's saved target address. + * + * @retval non-NULL success + * @retval NULL no dynamic window regions are defined. + */ +struct bhndb_dw_alloc * +bhndb_dw_steal(struct bhndb_resources *br, bus_addr_t *saved) +{ + struct bhndb_dw_alloc *dw_stolen; + + KASSERT(bhndb_dw_next_free(br) == NULL, + ("attempting to steal an in-use window while free windows remain")); + + /* Nothing to steal from? */ + if (br->dwa_count == 0) + return (NULL); + + /* + * Acquire our steal spinlock; this will be released in + * bhndb_dw_return_stolen(). + * + * Acquiring also disables interrupts, which is required when one is + * stealing an in-use existing register window. + */ + mtx_lock_spin(&br->dw_steal_mtx); + + dw_stolen = &br->dw_alloc[0]; + *saved = dw_stolen->target; + return (dw_stolen); +} + +/** + * Return an allocation record previously stolen using bhndb_dw_steal(). + * + * @param dev The device on which to issue a BHNDB_SET_WINDOW_ADDR() request. + * @param br The resource state owning @p dwa. + * @param dwa The allocation record to be returned. + * @param saved The original target address provided by bhndb_dw_steal(). + */ +void +bhndb_dw_return_stolen(device_t dev, struct bhndb_resources *br, + struct bhndb_dw_alloc *dwa, bus_addr_t saved) +{ + int error; + + mtx_assert(&br->dw_steal_mtx, MA_OWNED); + + error = bhndb_dw_set_addr(dev, br, dwa, saved, 0); + if (error) { + panic("failed to restore register window target %#jx: %d\n", + (uintmax_t)saved, error); + } + + mtx_unlock_spin(&br->dw_steal_mtx); +} + +/** * Return the count of @p type register windows in @p table. * * @param table The table to search. @@ -1380,18 +1492,24 @@ bhndb_regwin_find_type(const struct bhndb_regwin *tabl * @param port_type The required port type. * @param port The required port. * @param region The required region. + * @param offset The required readable core register block offset. + * @param min_size The required minimum readable size at @p offset. * * @retval bhndb_regwin The first matching window. * @retval NULL If no matching window was found. */ const struct bhndb_regwin * bhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class, - int unit, bhnd_port_type port_type, u_int port, u_int region) + int unit, bhnd_port_type port_type, u_int port, u_int region, + bus_size_t offset, bus_size_t min_size) { const struct bhndb_regwin *rw; - + for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { + bus_size_t rw_offset; + + /* Match on core, port, and region attributes */ if (rw->win_type != BHNDB_REGWIN_T_CORE) continue; @@ -1410,6 +1528,19 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl if (rw->d.core.region != region) continue; + /* Verify that the requested range is mapped within + * this register window */ + if (rw->d.core.offset > offset) + continue; + + rw_offset = offset - rw->d.core.offset; + + if (rw->win_size < rw_offset) + continue; + + if (rw->win_size - rw_offset < min_size) + continue; + return (rw); } @@ -1429,7 +1560,8 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl * @param port_type The required port type. * @param port The required port. * @param region The required region. - * @param min_size The minimum window size. + * @param offset The required readable core register block offset. + * @param min_size The required minimum readable size at @p offset. * * @retval bhndb_regwin The first matching window. * @retval NULL If no matching window was found. @@ -1437,13 +1569,13 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl const struct bhndb_regwin * bhndb_regwin_find_best(const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, - u_int region, bus_size_t min_size) + u_int region, bus_size_t offset, bus_size_t min_size) { const struct bhndb_regwin *rw; /* Prefer a fixed core mapping */ rw = bhndb_regwin_find_core(table, class, unit, port_type, - port, region); + port, region, offset, min_size); if (rw != NULL) return (rw); @@ -1494,6 +1626,45 @@ bhndb_hw_priority_find_core(const struct bhndb_hw_prio for (hp = table; hp->ports != NULL; hp++) { if (bhnd_core_matches(core, &hp->match)) return (hp); + } + + /* not found */ + return (NULL); +} + + +/** + * Search for a port resource priority descriptor in @p table. + * + * @param table The table to search. + * @param core The core to match against @p table. + * @param port_type The required port type. + * @param port The required port. + * @param region The required region. + */ +const struct bhndb_port_priority * +bhndb_hw_priorty_find_port(const struct bhndb_hw_priority *table, + struct bhnd_core_info *core, bhnd_port_type port_type, u_int port, + u_int region) +{ + const struct bhndb_hw_priority *hp; + + if ((hp = bhndb_hw_priority_find_core(table, core)) == NULL) + return (NULL); + + for (u_int i = 0; i < hp->num_ports; i++) { + const struct bhndb_port_priority *pp = &hp->ports[i]; + + if (pp->type != port_type) + continue; + + if (pp->port != port) + continue; + + if (pp->region != region) + continue; + + return (pp); } /* not found */ Modified: head/sys/dev/bhnd/bhndb/bhndbvar.h ============================================================================== --- head/sys/dev/bhnd/bhndb/bhndbvar.h Mon Nov 27 23:48:21 2017 (r326296) +++ head/sys/dev/bhnd/bhndb/bhndbvar.h Tue Nov 28 00:12:14 2017 (r326297) @@ -118,13 +118,15 @@ const struct bhndb_regwin *bhndb_regwin_find_core( const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, - u_int region); + u_int region, bus_size_t offset, + bus_size_t min_size); const struct bhndb_regwin *bhndb_regwin_find_best( const struct bhndb_regwin *table, bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, - u_int region, bus_size_t min_size); + u_int region, bus_size_t offset, + bus_size_t min_size); bool bhndb_regwin_match_core( const struct bhndb_regwin *regw,