From owner-svn-src-head@freebsd.org Tue Nov 21 23:15:22 2017 Return-Path: Delivered-To: svn-src-head@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 2D0DEDBA911; Tue, 21 Nov 2017 23:15:22 +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 BE09D3473; Tue, 21 Nov 2017 23:15:21 +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 vALNFKPB033030; Tue, 21 Nov 2017 23:15:20 GMT (envelope-from landonf@FreeBSD.org) Received: (from landonf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id vALNFK6G033023; Tue, 21 Nov 2017 23:15:20 GMT (envelope-from landonf@FreeBSD.org) Message-Id: <201711212315.vALNFK6G033023@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: landonf set sender to landonf@FreeBSD.org using -f From: "Landon J. Fuller" Date: Tue, 21 Nov 2017 23:15:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r326079 - in head/sys: dev/bhnd dev/bhnd/bcma dev/bhnd/bhndb dev/bhnd/cores/chipc dev/bhnd/cores/pci dev/bhnd/cores/usb dev/bhnd/siba mips/broadcom mips/include mips/mips X-SVN-Group: head X-SVN-Commit-Author: landonf X-SVN-Commit-Paths: in head/sys: dev/bhnd dev/bhnd/bcma dev/bhnd/bhndb dev/bhnd/cores/chipc dev/bhnd/cores/pci dev/bhnd/cores/usb dev/bhnd/siba mips/broadcom mips/include mips/mips X-SVN-Commit-Revision: 326079 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Nov 2017 23:15:22 -0000 Author: landonf Date: Tue Nov 21 23:15:20 2017 New Revision: 326079 URL: https://svnweb.freebsd.org/changeset/base/326079 Log: bhnd(4): implement MIPS and PCI(e) interrupt support On BHND MIPS SoCs, this replaces the use of hard-coded MIPS IRQ#s in the common bhnd(4) core drivers; we now register an INTRNG child PIC that handles routing of backplane interrupt vectors via the MIPS core. On BHND PCI devices, backplane interrupt vectors are now routed to the PCI/PCIe host bridge core when bus_setup_intr() is called, where they are dispatched by the PCI core via a host interrupt (e.g. INTx/MSI). The bhndb(4) bridge driver tracks registered interrupt handlers for the bridged bhnd(4) devices and manages backplane interrupt routing, while delegating actual bus interrupt setup/teardown to the parent bus on behalf of the bridged cores. Approved by: adrian (mentor, implicit) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D12518 Added: head/sys/mips/broadcom/bcm_mips.c (contents, props changed) head/sys/mips/broadcom/bcm_mipsvar.h (contents, props changed) Modified: head/sys/dev/bhnd/bcma/bcma.c head/sys/dev/bhnd/bcma/bcma_subr.c head/sys/dev/bhnd/bcma/bcmavar.h head/sys/dev/bhnd/bhnd.c head/sys/dev/bhnd/bhnd.h head/sys/dev/bhnd/bhnd_bus_if.m head/sys/dev/bhnd/bhnd_ids.h head/sys/dev/bhnd/bhnd_match.h head/sys/dev/bhnd/bhnd_subr.c head/sys/dev/bhnd/bhndb/bhnd_bhndb.c head/sys/dev/bhnd/bhndb/bhndb.c head/sys/dev/bhnd/bhndb/bhndb_if.m head/sys/dev/bhnd/bhndb/bhndb_pci.c head/sys/dev/bhnd/bhndb/bhndb_pcireg.h head/sys/dev/bhnd/bhndb/bhndb_pcivar.h head/sys/dev/bhnd/bhndb/bhndb_private.h head/sys/dev/bhnd/bhndb/bhndb_subr.c head/sys/dev/bhnd/bhndb/bhndbvar.h head/sys/dev/bhnd/bhndvar.h head/sys/dev/bhnd/cores/chipc/chipc.c head/sys/dev/bhnd/cores/chipc/chipc_private.h head/sys/dev/bhnd/cores/chipc/chipc_subr.c head/sys/dev/bhnd/cores/chipc/chipcvar.h head/sys/dev/bhnd/cores/pci/bhnd_pci_hostb.c head/sys/dev/bhnd/cores/usb/bhnd_usb.c head/sys/dev/bhnd/cores/usb/bhnd_usbvar.h head/sys/dev/bhnd/siba/siba.c head/sys/dev/bhnd/siba/siba_bhndb.c head/sys/dev/bhnd/siba/siba_erom.c head/sys/dev/bhnd/siba/siba_subr.c head/sys/dev/bhnd/siba/sibareg.h head/sys/dev/bhnd/siba/sibavar.h head/sys/mips/broadcom/bcm_bmips.c head/sys/mips/broadcom/bcm_machdep.c head/sys/mips/broadcom/bcm_machdep.h head/sys/mips/broadcom/bcm_mips74k.c head/sys/mips/broadcom/bcm_mips74kreg.h head/sys/mips/broadcom/bcma_nexus.c head/sys/mips/broadcom/bhnd_nexus.c head/sys/mips/broadcom/files.broadcom head/sys/mips/broadcom/siba_nexus.c head/sys/mips/include/intr.h head/sys/mips/mips/mips_pic.c Modified: head/sys/dev/bhnd/bcma/bcma.c ============================================================================== --- head/sys/dev/bhnd/bcma/bcma.c Tue Nov 21 22:06:49 2017 (r326078) +++ head/sys/dev/bhnd/bcma/bcma.c Tue Nov 21 23:15:20 2017 (r326079) @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -124,7 +128,7 @@ bcma_child_deleted(device_t dev, device_t child) /* Free bcma device info */ if ((dinfo = device_get_ivars(child)) != NULL) - bcma_free_dinfo(dev, dinfo); + bcma_free_dinfo(dev, child, dinfo); device_set_ivars(child, NULL); } @@ -613,66 +617,46 @@ bcma_get_region_addr(device_t dev, device_t child, bhn /** * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT(). - * - * This implementation consults @p child's agent register block, - * returning the number of interrupt output lines routed to @p child. */ -int +u_int bcma_get_intr_count(device_t dev, device_t child) { - struct bcma_devinfo *dinfo; - uint32_t dmpcfg, oobw; + struct bcma_devinfo *dinfo; - dinfo = device_get_ivars(child); + /* delegate non-bus-attached devices to our parent */ + if (device_get_parent(child) != dev) + return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child)); - /* Agent block must be mapped */ - if (dinfo->res_agent == NULL) - return (0); - - /* Agent must support OOB */ - dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG); - if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB)) - return (0); - - /* Return OOB width as interrupt count */ - oobw = bhnd_bus_read_4(dinfo->res_agent, - BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR)); - if (oobw > BCMA_OOB_NUM_SEL) { - device_printf(dev, "ignoring invalid OOBOUTWIDTH for core %u: " - "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw); - return (0); - } - - return (oobw); + dinfo = device_get_ivars(child); + return (dinfo->num_intrs); } /** - * Default bcma(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC(). - * - * This implementation consults @p child's agent register block, - * returning the interrupt output line routed to @p child, at OOB selector - * @p intr. + * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC(). */ int -bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec) +bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec) { struct bcma_devinfo *dinfo; - uint32_t oobsel; + struct bcma_intr *desc; + /* delegate non-bus-attached devices to our parent */ + if (device_get_parent(child) != dev) { + return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child, + intr, ivec)); + } + dinfo = device_get_ivars(child); - /* Interrupt ID must be valid. */ - if (intr >= bcma_get_intr_count(dev, child)) - return (ENXIO); + STAILQ_FOREACH(desc, &dinfo->intrs, i_link) { + if (desc->i_sel == intr) { + *ivec = desc->i_busline; + return (0); + } + } - /* Fetch OOBSEL busline value */ - KASSERT(dinfo->res_agent != NULL, ("missing agent registers")); - oobsel = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT( - BCMA_OOB_BANK_INTR, intr)); - *ivec = (oobsel >> BCMA_DMP_OOBSEL_SHIFT(intr)) & - BCMA_DMP_OOBSEL_BUSLINE_MASK; - - return (0); + /* Not found */ + return (ENXIO); } /** @@ -707,8 +691,6 @@ bcma_add_children(device_t bus) /* Add all cores. */ bcma_erom = (struct bcma_erom *)erom; while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) { - int nintr; - /* Add the child device */ child = BUS_ADD_CHILD(bus, 0, NULL, -1); if (child == NULL) { @@ -718,27 +700,12 @@ bcma_add_children(device_t bus) /* Initialize device ivars */ dinfo = device_get_ivars(child); - if ((error = bcma_init_dinfo(bus, dinfo, corecfg))) + if ((error = bcma_init_dinfo(bus, child, dinfo, corecfg))) goto cleanup; /* The dinfo instance now owns the corecfg value */ corecfg = NULL; - /* Allocate device's agent registers, if any */ - if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo))) - goto cleanup; - - /* Assign interrupts */ - nintr = bhnd_get_intr_count(child); - for (int rid = 0; rid < nintr; rid++) { - error = BHND_BUS_ASSIGN_INTR(bus, child, rid); - if (error) { - device_printf(bus, "failed to assign interrupt " - "%d to core %u: %d\n", rid, - BCMA_DINFO_COREIDX(dinfo), error); - } - } - /* If pins are floating or the hardware is otherwise * unpopulated, the device shouldn't be used. */ if (bhnd_is_hw_disabled(child)) @@ -794,7 +761,7 @@ static device_method_t bcma_methods[] = { DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid), DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr), DEVMETHOD(bhnd_bus_get_intr_count, bcma_get_intr_count), - DEVMETHOD(bhnd_bus_get_core_ivec, bcma_get_core_ivec), + DEVMETHOD(bhnd_bus_get_intr_ivec, bcma_get_intr_ivec), DEVMETHOD_END }; Modified: head/sys/dev/bhnd/bcma/bcma_subr.c ============================================================================== --- head/sys/dev/bhnd/bcma/bcma_subr.c Tue Nov 21 22:06:49 2017 (r326078) +++ head/sys/dev/bhnd/bcma/bcma_subr.c Tue Nov 21 23:15:20 2017 (r326079) @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -154,7 +158,7 @@ bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, b * @param ports The set of ports to be enumerated */ static void -bcma_dinfo_init_resource_info(device_t bus, struct bcma_devinfo *dinfo, +bcma_dinfo_init_port_resource_info(device_t bus, struct bcma_devinfo *dinfo, struct bcma_sport_list *ports) { struct bcma_map *map; @@ -193,7 +197,127 @@ bcma_dinfo_init_resource_info(device_t bus, struct bcm } + /** + * Allocate the per-core agent register block for a device info structure. + * + * If an agent0.0 region is not defined on @p dinfo, the device info + * agent resource is set to NULL and 0 is returned. + * + * @param bus The requesting bus device. + * @param child The bcma child device. + * @param dinfo The device info associated with @p child + * + * @retval 0 success + * @retval non-zero resource allocation failed. + */ +static int +bcma_dinfo_init_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo) +{ + bhnd_addr_t addr; + bhnd_size_t size; + rman_res_t r_start, r_count, r_end; + int error; + + KASSERT(dinfo->res_agent == NULL, ("double allocation of agent")); + + /* Verify that the agent register block exists and is + * mappable */ + if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) + return (0); /* nothing to do */ + + /* Fetch the address of the agent register block */ + error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0, + &addr, &size); + if (error) { + device_printf(bus, "failed fetching agent register block " + "address for core %u\n", BCMA_DINFO_COREIDX(dinfo)); + return (error); + } + + /* Allocate the resource */ + r_start = addr; + r_count = size; + r_end = r_start + r_count - 1; + + dinfo->rid_agent = BCMA_AGENT_RID(dinfo); + dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY, + &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE|RF_SHAREABLE); + if (dinfo->res_agent == NULL) { + device_printf(bus, "failed allocating agent register block for " + "core %u\n", BCMA_DINFO_COREIDX(dinfo)); + return (ENXIO); + } + + return (0); +} + +/** + * Populate the list of interrupts for a device info structure + * previously initialized via bcma_dinfo_alloc_agent(). + * + * If an agent0.0 region is not mapped on @p dinfo, the OOB interrupt bank is + * assumed to be unavailable and 0 is returned. + * + * @param bus The requesting bus device. + * @param dinfo The device info instance to be initialized. + */ +static int +bcma_dinfo_init_intrs(device_t bus, device_t child, + struct bcma_devinfo *dinfo) +{ + uint32_t dmpcfg, oobw; + + /* Agent block must be mapped */ + if (dinfo->res_agent == NULL) + return (0); + + /* Agent must support OOB */ + dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG); + if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB)) + return (0); + + /* Fetch width of the OOB interrupt bank */ + oobw = bhnd_bus_read_4(dinfo->res_agent, + BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR)); + if (oobw > BCMA_OOB_NUM_SEL) { + device_printf(bus, "ignoring invalid OOBOUTWIDTH for core %u: " + "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw); + return (0); + } + + /* Fetch OOBSEL busline values and populate list of interrupt + * descriptors */ + for (uint32_t sel = 0; sel < oobw; sel++) { + struct bcma_intr *intr; + uint32_t selout; + uint8_t line; + + if (dinfo->num_intrs == UINT_MAX) + return (ENOMEM); + + selout = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT( + BCMA_OOB_BANK_INTR, sel)); + + line = (selout >> BCMA_DMP_OOBSEL_SHIFT(sel)) & + BCMA_DMP_OOBSEL_BUSLINE_MASK; + + intr = bcma_alloc_intr(BCMA_OOB_BANK_INTR, sel, line); + if (intr == NULL) { + device_printf(bus, "failed allocating interrupt " + "descriptor %#x for core %u\n", sel, + BCMA_DINFO_COREIDX(dinfo)); + return (ENOMEM); + } + + STAILQ_INSERT_HEAD(&dinfo->intrs, intr, i_link); + dinfo->num_intrs++; + } + + return (0); +} + +/** * Allocate and return a new empty device info structure. * * @param bus The requesting bus device. @@ -213,6 +337,9 @@ bcma_alloc_dinfo(device_t bus) dinfo->res_agent = NULL; dinfo->rid_agent = -1; + STAILQ_INIT(&dinfo->intrs); + dinfo->num_intrs = 0; + resource_list_init(&dinfo->resources); return (dinfo); @@ -224,7 +351,8 @@ bcma_alloc_dinfo(device_t bus) * configuration. * * @param bus The requesting bus device. - * @param dinfo The device info instance. + * @param child The bcma child device. + * @param dinfo The device info associated with @p child * @param corecfg Device core configuration; ownership of this value * will be assumed by @p dinfo. * @@ -232,9 +360,12 @@ bcma_alloc_dinfo(device_t bus) * @retval non-zero initialization failed. */ int -bcma_init_dinfo(device_t bus, struct bcma_devinfo *dinfo, +bcma_init_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo, struct bcma_corecfg *corecfg) { + struct bcma_intr *intr; + int error; + KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized")); /* Save core configuration value */ @@ -242,71 +373,52 @@ bcma_init_dinfo(device_t bus, struct bcma_devinfo *din /* The device ports must always be initialized first to ensure that * rid 0 maps to the first device port */ - bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->dev_ports); + bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->dev_ports); + bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->bridge_ports); + bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->wrapper_ports); - bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->bridge_ports); - bcma_dinfo_init_resource_info(bus, dinfo, &corecfg->wrapper_ports); + /* Now that we've defined the port resources, we can map the device's + * agent registers (if any) */ + if ((error = bcma_dinfo_init_agent(bus, child, dinfo))) + goto failed; - return (0); -} + /* With agent registers mapped, we can populate the device's interrupt + * descriptors */ + if ((error = bcma_dinfo_init_intrs(bus, child, dinfo))) + goto failed; + /* Finally, map the interrupt descriptors */ + STAILQ_FOREACH(intr, &dinfo->intrs, i_link) { + /* Already mapped? */ + if (intr->i_mapped) + continue; -/** - * Allocate the per-core agent register block for a device info structure - * previous initialized via bcma_init_dinfo(). - * - * If an agent0.0 region is not defined on @p dinfo, the device info - * agent resource is set to NULL and 0 is returned. - * - * @param bus The requesting bus device. - * @param child The bcma child device. - * @param dinfo The device info associated with @p child - * - * @retval 0 success - * @retval non-zero resource allocation failed. - */ -int -bcma_dinfo_alloc_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo) -{ - bhnd_addr_t addr; - bhnd_size_t size; - rman_res_t r_start, r_count, r_end; - int error; + /* Map the interrupt */ + error = BHND_BUS_MAP_INTR(bus, child, intr->i_sel, + &intr->i_irq); + if (error) { + device_printf(bus, "failed mapping interrupt line %u " + "for core %u: %d\n", intr->i_sel, + BCMA_DINFO_COREIDX(dinfo), error); + goto failed; + } - KASSERT(dinfo->res_agent == NULL, ("double allocation of agent")); + intr->i_mapped = true; - /* Verify that the agent register block exists and is - * mappable */ - if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) - return (0); /* nothing to do */ - - /* Fetch the address of the agent register block */ - error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0, - &addr, &size); - if (error) { - device_printf(bus, "failed fetching agent register block " - "address for core %u\n", BCMA_DINFO_COREIDX(dinfo)); - return (error); + /* Add to resource list */ + intr->i_rid = resource_list_add_next(&dinfo->resources, + SYS_RES_IRQ, intr->i_irq, intr->i_irq, 1); } - /* Allocate the resource */ - r_start = addr; - r_count = size; - r_end = r_start + r_count - 1; + return (0); - dinfo->rid_agent = BCMA_AGENT_RID(dinfo); - dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY, - &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE); - if (dinfo->res_agent == NULL) { - device_printf(bus, "failed allocating agent register block for " - "core %u\n", BCMA_DINFO_COREIDX(dinfo)); - return (ENXIO); - } +failed: + /* Owned by the caller on failure */ + dinfo->corecfg = NULL; - return (0); + return (error); } - /** * Deallocate the given device info structure and any associated resources. * @@ -314,8 +426,10 @@ bcma_dinfo_alloc_agent(device_t bus, device_t child, s * @param dinfo Device info to be deallocated. */ void -bcma_free_dinfo(device_t bus, struct bcma_devinfo *dinfo) +bcma_free_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo) { + struct bcma_intr *intr, *inext; + resource_list_free(&dinfo->resources); if (dinfo->corecfg != NULL) @@ -327,9 +441,69 @@ bcma_free_dinfo(device_t bus, struct bcma_devinfo *din dinfo->res_agent); } + /* Clean up interrupt descriptors */ + STAILQ_FOREACH_SAFE(intr, &dinfo->intrs, i_link, inext) { + STAILQ_REMOVE(&dinfo->intrs, intr, bcma_intr, i_link); + + /* Release our IRQ mapping */ + if (intr->i_mapped) { + BHND_BUS_UNMAP_INTR(bus, child, intr->i_irq); + intr->i_mapped = false; + } + + bcma_free_intr(intr); + } + free(dinfo, M_BHND); } + +/** + * Allocate and initialize a new interrupt descriptor. + * + * @param bank OOB bank. + * @param sel OOB selector. + * @param line OOB bus line. + */ +struct bcma_intr * +bcma_alloc_intr(uint8_t bank, uint8_t sel, uint8_t line) +{ + struct bcma_intr *intr; + + if (bank >= BCMA_OOB_NUM_BANKS) + return (NULL); + + if (sel >= BCMA_OOB_NUM_SEL) + return (NULL); + + if (line >= BCMA_OOB_NUM_BUSLINES) + return (NULL); + + intr = malloc(sizeof(*intr), M_BHND, M_NOWAIT); + if (intr == NULL) + return (NULL); + + intr->i_bank = bank; + intr->i_sel = sel; + intr->i_busline = line; + intr->i_mapped = false; + intr->i_irq = 0; + + return (intr); +} + +/** + * Deallocate all resources associated with the given interrupt descriptor. + * + * @param intr Interrupt descriptor to be deallocated. + */ +void +bcma_free_intr(struct bcma_intr *intr) +{ + KASSERT(!intr->i_mapped, ("interrupt %u still mapped", intr->i_sel)); + + free(intr, M_BHND); +} /** * Allocate and initialize new slave port descriptor. Modified: head/sys/dev/bhnd/bcma/bcmavar.h ============================================================================== --- head/sys/dev/bhnd/bcma/bcmavar.h Tue Nov 21 22:06:49 2017 (r326078) +++ head/sys/dev/bhnd/bcma/bcmavar.h Tue Nov 21 23:15:20 2017 (r326079) @@ -1,7 +1,11 @@ /*- - * Copyright (c) 2015 Landon Fuller + * Copyright (c) 2015-2016 Landon Fuller + * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Landon Fuller + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -67,6 +71,7 @@ typedef u_int bcma_rmid_t; struct bcma_devinfo; struct bcma_corecfg; +struct bcma_intr; struct bcma_map; struct bcma_mport; struct bcma_sport; @@ -74,8 +79,8 @@ struct bcma_sport; int bcma_probe(device_t dev); int bcma_attach(device_t dev); int bcma_detach(device_t dev); -int bcma_get_intr_count(device_t dev, device_t child); -int bcma_get_core_ivec(device_t dev, device_t child, +u_int bcma_get_intr_count(device_t dev, device_t child); +int bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec); int bcma_add_children(device_t bus); @@ -84,18 +89,20 @@ struct bcma_sport_list *bcma_corecfg_get_port_list(str bhnd_port_type type); struct bcma_devinfo *bcma_alloc_dinfo(device_t bus); -int bcma_init_dinfo(device_t bus, +int bcma_init_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo, struct bcma_corecfg *corecfg); -int bcma_dinfo_alloc_agent(device_t bus, device_t child, +void bcma_free_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo); -void bcma_free_dinfo(device_t bus, - struct bcma_devinfo *dinfo); struct bcma_corecfg *bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor, uint16_t device, uint8_t hwrev); void bcma_free_corecfg(struct bcma_corecfg *corecfg); +struct bcma_intr *bcma_alloc_intr(uint8_t bank, uint8_t sel, + uint8_t line); +void bcma_free_intr(struct bcma_intr *intr); + struct bcma_sport *bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type); void bcma_free_sport(struct bcma_sport *sport); @@ -121,6 +128,18 @@ struct bcma_map { STAILQ_ENTRY(bcma_map) m_link; }; +/** BCMA interrupt descriptor */ +struct bcma_intr { + uint8_t i_bank; /**< OOB bank (see BCMA_OOB_BANK[A-D]) */ + uint8_t i_sel; /**< OOB selector (0-7) */ + uint8_t i_busline; /**< OOB bus line assigned to this selector */ + bool i_mapped; /**< if an irq has been mapped for this selector */ + int i_rid; /**< bus resource id, or -1 */ + rman_res_t i_irq; /**< the mapped bus irq, if any */ + + STAILQ_ENTRY(bcma_intr) i_link; +}; + /** BCMA slave port descriptor */ struct bcma_sport { bcma_pid_t sp_num; /**< slave port number (core-unique) */ @@ -131,8 +150,9 @@ struct bcma_sport { STAILQ_ENTRY(bcma_sport) sp_link; }; -STAILQ_HEAD(bcma_mport_list, bcma_mport); -STAILQ_HEAD(bcma_sport_list, bcma_sport); +STAILQ_HEAD(bcma_mport_list, bcma_mport); +STAILQ_HEAD(bcma_intr_list, bcma_intr); +STAILQ_HEAD(bcma_sport_list, bcma_sport); /** BCMA IP core/block configuration */ struct bcma_corecfg { @@ -161,6 +181,9 @@ struct bcma_devinfo { struct bhnd_resource *res_agent; /**< Agent (wrapper) resource, or NULL. Not * all bcma(4) cores have or require an agent. */ int rid_agent; /**< Agent resource ID, or -1 */ + + u_int num_intrs; /**< number of interrupt descriptors. */ + struct bcma_intr_list intrs; /**< interrupt descriptors */ struct bhnd_core_pmu_info *pmu_info; /**< Bus-managed PMU state, or NULL */ }; Modified: head/sys/dev/bhnd/bhnd.c ============================================================================== --- head/sys/dev/bhnd/bhnd.c Tue Nov 21 22:06:49 2017 (r326078) +++ head/sys/dev/bhnd/bhnd.c Tue Nov 21 23:15:20 2017 (r326079) @@ -814,6 +814,22 @@ bhnd_generic_resume_child(device_t dev, device_t child return bus_generic_resume_child(dev, child); } + +/** + * Default bhnd(4) bus driver implementation of BUS_SETUP_INTR(). + * + * This implementation of BUS_SETUP_INTR() will delegate interrupt setup + * to the parent of @p dev, if any. + */ +int +bhnd_generic_setup_intr(device_t dev, device_t child, struct resource *irq, + int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg, + void **cookiep) +{ + return (bus_generic_setup_intr(dev, child, irq, flags, filter, intr, + arg, cookiep)); +} + /* * Delegate all indirect I/O to the parent device. When inherited by * non-bridged bus implementations, resources will never be marked as @@ -917,7 +933,7 @@ static device_method_t bhnd_methods[] = { DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), - DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_setup_intr, bhnd_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_config_intr, bus_generic_config_intr), DEVMETHOD(bus_bind_intr, bus_generic_bind_intr), Modified: head/sys/dev/bhnd/bhnd.h ============================================================================== --- head/sys/dev/bhnd/bhnd.h Tue Nov 21 22:06:49 2017 (r326078) +++ head/sys/dev/bhnd/bhnd.h Tue Nov 21 23:15:20 2017 (r326079) @@ -250,10 +250,10 @@ struct bhnd_device_quirk { {{ BHND_MATCH_CORE_REV(_rev) }, (_flags) } #define BHND_CHIP_QUIRK(_chip, _rev, _flags) \ - {{ BHND_CHIP_IR(BCM ## _chip, _rev) }, (_flags) } + {{ BHND_MATCH_CHIP_IR(BCM ## _chip, _rev) }, (_flags) } #define BHND_PKG_QUIRK(_chip, _pkg, _flags) \ - {{ BHND_CHIP_IP(BCM ## _chip, BCM ## _chip ## _pkg) }, (_flags) } + {{ BHND_MATCH_CHIP_IP(BCM ## _chip, BCM ## _chip ## _pkg) }, (_flags) } #define BHND_BOARD_QUIRK(_board, _flags) \ {{ BHND_MATCH_BOARD_TYPE(_board) }, \ @@ -528,8 +528,8 @@ int bhnd_bus_generic_activate_resource (device_t d int bhnd_bus_generic_deactivate_resource (device_t dev, device_t child, int type, int rid, struct bhnd_resource *r); -bhnd_attach_type bhnd_bus_generic_get_attach_type(device_t dev, - device_t child); +uintptr_t bhnd_bus_generic_get_intr_domain(device_t dev, + device_t child, bool self); /** * Return the bhnd(4) bus driver's device enumeration parser class @@ -865,25 +865,22 @@ bhnd_read_board_info(device_t dev, struct bhnd_board_i } /** - * Return the number of interrupts to be assigned to @p child via - * BHND_BUS_ASSIGN_INTR(). + * Return the number of interrupt lines assigned to @p dev. * * @param dev A bhnd bus child device. */ -static inline int +static inline u_int bhnd_get_intr_count(device_t dev) { return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), dev)); } /** - * Return the backplane interrupt vector corresponding to @p dev's given - * @p intr number. + * Get the backplane interrupt vector of the @p intr line attached to @p dev. * * @param dev A bhnd bus child device. - * @param intr The interrupt number being queried. This is equivalent to the - * bus resource ID for the interrupt. - * @param[out] ivec On success, the assigned hardware interrupt vector be + * @param intr The index of the interrupt line being queried. + * @param[out] ivec On success, the assigned hardware interrupt vector will be * written to this pointer. * * On bcma(4) devices, this returns the OOB bus line assigned to the @@ -893,14 +890,48 @@ bhnd_get_intr_count(device_t dev) * to the interrupt. * * @retval 0 success - * @retval ENXIO If @p intr exceeds the number of interrupts available - * to @p child. + * @retval ENXIO If @p intr exceeds the number of interrupt lines + * assigned to @p child. */ static inline int -bhnd_get_core_ivec(device_t dev, u_int intr, uint32_t *ivec) +bhnd_get_intr_ivec(device_t dev, u_int intr, u_int *ivec) { - return (BHND_BUS_GET_CORE_IVEC(device_get_parent(dev), dev, intr, + return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), dev, intr, ivec)); +} + +/** + * Map the given @p intr to an IRQ number; until unmapped, this IRQ may be used + * to allocate a resource of type SYS_RES_IRQ. + * + * On success, the caller assumes ownership of the interrupt mapping, and + * is responsible for releasing the mapping via bhnd_unmap_intr(). + * + * @param dev The requesting device. + * @param intr The interrupt being mapped. + * @param[out] irq On success, the bus interrupt value mapped for @p intr. + * + * @retval 0 If an interrupt was assigned. + * @retval non-zero If mapping an interrupt otherwise fails, a regular + * unix error code will be returned. + */ +static inline int +bhnd_map_intr(device_t dev, u_int intr, rman_res_t *irq) +{ + return (BHND_BUS_MAP_INTR(device_get_parent(dev), dev, intr, irq)); +} + +/** + * Unmap an bus interrupt previously mapped via bhnd_map_intr(). + * + * @param dev The requesting device. + * @param intr The interrupt number being unmapped. This is equivalent to the + * bus resource ID for the interrupt. + */ +static inline void +bhnd_unmap_intr(device_t dev, rman_res_t irq) +{ + return (BHND_BUS_UNMAP_INTR(device_get_parent(dev), dev, irq)); } /** Modified: head/sys/dev/bhnd/bhnd_bus_if.m ============================================================================== --- head/sys/dev/bhnd/bhnd_bus_if.m Tue Nov 21 22:06:49 2017 (r326078) +++ head/sys/dev/bhnd/bhnd_bus_if.m Tue Nov 21 23:15:20 2017 (r326079) @@ -141,25 +141,6 @@ CODE { panic("bhnd_bus_read_boardinfo unimplemented"); } - static int - bhnd_bus_null_get_intr_count(device_t dev, device_t child) - { - panic("bhnd_bus_get_intr_count unimplemented"); - } - - static int - bhnd_bus_null_assign_intr(device_t dev, device_t child, int rid) - { - panic("bhnd_bus_assign_intr unimplemented"); - } - - static int - bhnd_bus_null_get_core_ivec(device_t dev, device_t child, u_int intr, - uint32_t *ivec) - { - panic("bhnd_bus_get_core_ivec unimplemented"); - } - static void bhnd_bus_null_child_added(device_t dev, device_t child) { @@ -243,7 +224,40 @@ CODE { panic("bhnd_bus_get_probe_order unimplemented"); } + static uintptr_t + bhnd_bus_null_get_intr_domain(device_t dev, device_t child, bool self) + { + /* Unsupported */ + return (0); + } + + static u_int + bhnd_bus_null_get_intr_count(device_t dev, device_t child) + { + return (0); + } + static int + bhnd_bus_null_get_intr_ivec(device_t dev, device_t child, u_int intr, + u_int *ivec) + { + panic("bhnd_bus_get_intr_ivec unimplemented"); + } + + static int + bhnd_bus_null_map_intr(device_t dev, device_t child, u_int intr, + rman_res_t *irq) + { + panic("bhnd_bus_map_intr unimplemented"); + } + + static int + bhnd_bus_null_unmap_intr(device_t dev, device_t child, rman_res_t irq) + { + panic("bhnd_bus_unmap_intr unimplemented"); + } + + static int bhnd_bus_null_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, u_int port, u_int region) { @@ -488,77 +502,6 @@ METHOD int read_board_info { } DEFAULT bhnd_bus_null_read_board_info; /** - * Return the number of interrupts to be assigned to @p child via - * BHND_BUS_ASSIGN_INTR(). - * - * @param dev The bhnd bus parent of @p child. - * @param child The bhnd device for which a count should be returned. - * - * @retval 0 If no interrupts should be assigned. - * @retval non-zero The count of interrupt resource IDs to be - * assigned, starting at rid 0. - */ -METHOD int get_intr_count { - device_t dev; - device_t child; -} DEFAULT bhnd_bus_null_get_intr_count; - -/** - * Assign an interrupt to @p child via bus_set_resource(). - * - * The default bus implementation of this method should assign backplane - * interrupt values to @p child. - * - * Bridge-attached bus implementations may instead override standard - * interconnect IRQ assignment, providing IRQs inherited from the parent bus. - * - * TODO: Once we can depend on INTRNG, investigate replacing this with a - * bridge-level interrupt controller. - * - * @param dev The bhnd bus parent of @p child. - * @param child The bhnd device to which an interrupt should be assigned. - * @param rid The interrupt resource ID to be assigned. - * - * @retval 0 If an interrupt was assigned. - * @retval non-zero If assigning an interrupt otherwise fails, a regular - * unix error code will be returned. - */ -METHOD int assign_intr { - device_t dev; - device_t child; - int rid; -} DEFAULT bhnd_bus_null_assign_intr; - -/** - * Return the backplane interrupt vector corresponding to @p child's given - * @p intr number. - * - * @param dev The bhnd bus parent of @p child. - * @param child The bhnd device for which the assigned interrupt vector should - * be queried. - * @param intr The interrupt number being queried. This is equivalent to the - * bus resource ID for the interrupt. - * @param[out] ivec On success, the assigned hardware interrupt vector be - * written to this pointer. - * - * On bcma(4) devices, this returns the OOB bus line assigned to the - * interrupt. - * - * On siba(4) devices, this returns the target OCP slave flag number assigned - * to the interrupt. - * - * @retval 0 success - * @retval ENXIO If @p intr exceeds the number of interrupts available - * to @p child. - */ -METHOD int get_core_ivec { - device_t dev; - device_t child; - u_int intr; - uint32_t *ivec; -} DEFAULT bhnd_bus_null_get_core_ivec; - -/** * Notify a bhnd bus that a child was added. * * This method must be called by concrete bhnd(4) driver impementations @@ -996,6 +939,106 @@ METHOD int deactivate_resource { int rid; struct bhnd_resource *r; } DEFAULT bhnd_bus_generic_deactivate_resource; + +/** + * Return the interrupt domain. + * + * This globally unique value may be used as the interrupt controller 'xref' + * on targets that support INTRNG. + * + * @param dev The device whose child is being examined. + * @param child The child device. + * @parem self If true, return @p child's interrupt domain, rather than the + * domain in which @p child resides. + * + * On Non-OFW targets, this should either return: + * - The pointer address of a device that can uniquely identify @p child's + * interrupt domain (e.g., the bhnd bus' device_t address), or + * - 0 if unsupported by the bus. + * + * On OFW (including FDT) targets, this should return the @p child's iparent + * property's xref if @p self is false, the child's own node xref value if + * @p self is true, or 0 if no interrupt parent is found. + */ +METHOD uintptr_t get_intr_domain { + device_t dev; + device_t child; + bool self; +} DEFAULT bhnd_bus_null_get_intr_domain; + +/** + * Return the number of interrupt lines assigned to @p child. + * + * @param dev The bhnd device whose child is being examined. + * @param child The child device. + */ +METHOD u_int get_intr_count { + device_t dev; + device_t child; +} DEFAULT bhnd_bus_null_get_intr_count; + +/** + * Get the backplane interrupt vector of the @p intr line attached to @p child. + * + * @param dev The device whose child is being examined. + * @param child The child device. + * @param intr The index of the interrupt line being queried. + * @param[out] ivec On success, the assigned hardware interrupt vector will be + * written to this pointer. + * + * On bcma(4) devices, this returns the OOB bus line assigned to the + * interrupt. + * + * On siba(4) devices, this returns the target OCP slave flag number assigned + * to the interrupt. + * + * @retval 0 success + * @retval ENXIO If @p intr exceeds the number of interrupt lines + * assigned to @p child. + */ +METHOD int get_intr_ivec { + device_t dev; + device_t child; + u_int intr; + u_int *ivec; +} DEFAULT bhnd_bus_null_get_intr_ivec; + +/** + * Map the given @p intr to an IRQ number; until unmapped, this IRQ may be used + * to allocate a resource of type SYS_RES_IRQ. + * + * On success, the caller assumes ownership of the interrupt mapping, and *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***