Index: sys/arm/annapurna/alpine/alpine_pci.h =================================================================== --- /dev/null +++ sys/arm/annapurna/alpine/alpine_pci.h @@ -0,0 +1,51 @@ +/*- +******************************************************************************** +Copyright (C) 2015,2016 Annapurna Labs Ltd. + +This file may be licensed under the terms of the Annapurna Labs Commercial +License Agreement. + +Alternatively, this file can be distributed under the terms of the GNU General +Public License V2 as published by the Free Software Foundation and can be +found at http://www.gnu.org/licenses/gpl-2.0.html + +Alternatively, redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#ifndef ALPINE_PCI_H_ +#define ALPINE_PCI_H_ + +#define AL_PCI_RANGE_MAX 3 +#define AL_PCI_RANGE_BRIDGE 0 +#define AL_PCI_RANGE_MEM 1 +#define AL_PCI_RANGE_IO 2 + +#define TARGET_BUS_MASK 0xFF + +int al_msix_map_msi(int irq, uint64_t *addr, uint32_t *data); +int al_msix_alloc_msi(int count, int *irqs); +int al_msix_release_msi(int count, int *irqs); + +#endif /* ALPINE_PCI_H_ */ Index: sys/arm/annapurna/alpine/alpine_pci.c =================================================================== --- /dev/null +++ sys/arm/annapurna/alpine/alpine_pci.c @@ -0,0 +1,1507 @@ +/*- + * Copyright (c) 2008 MARVELL INTERNATIONAL LTD. + * Copyright (c) 2010 The FreeBSD Foundation + * Copyright (c) 2010-2016 Semihalf + * All rights reserved. + * + * Developed by Semihalf. + * + * Portions of this software were developed by Semihalf + * 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * Alpine PCI/PCI-Express controller driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ofw_bus_if.h" +#include "pcib_if.h" +#include "alpine_pci.h" +#include "contrib/alpine-hal/al_hal_unit_adapter_regs.h" +#include "contrib/alpine-hal/al_hal_pcie.h" + +#include +#include +#include "opt_alpine.h" + +/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */ +#define PCI_MIN_IO_ALLOC 4 +#define PCI_MIN_MEM_ALLOC 16 + +#define PCI_MAX_BUS_NUMBER 255 + +#define PCIE_MIN_MPS 128 +#define PCIE_MAX_MPS 4096 + +#define PCIE_MIN_MRRS 128 +#define PCIE_MAX_MRRS 4096 + +#define AL_PCI_PORTS 4 + +/* Maximum PCI/PCIE Memory */ +#define AL_PCI_MEM_SIZE (0x100000000) +#define AL_PCI_MEM_SLICE_SIZE (AL_PCI_MEM_SIZE / AL_PCI_PORTS) + +/* Maximum PCI/PCIE I/O */ +#define AL_PCI_IO_SIZE (0x100000) /* 1MB */ +#define AL_PCI_IO_SLICE_SIZE (AL_PCI_IO_SIZE / AL_PCI_PORTS) + +#define AL_PCI_MAX_SLOTS 16 +#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t)) +#define AL_MAX_SUPPORTED_MPS 128 + +#define AL_INTERNAL_BRIDGE_ECAM_BASE 0x2000 + +#define PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS 5 +#define PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS 12 + +#define FDT_RANGE_MEM 0x02000000 +#define FDT_RANGE_IO 0x01000000 + +#define AL_AXI_SLOT 15 +#define AL_AXI_FUNC 12 + +#define TARGET_BUS_MASK 0xFF +#define MAX_SUBORDINARY_BUS 0xFF + +#define CONFIG_ADDR_ZERO_BITS_MASK 0x3 +#define CONFIG_ADDR_BYTE_SELECTOR_MASK 0x3 + +#define BAR_SIZE_IN_32BIT_UNITS(bar) (((bar & 7) == 4) ? 2 : 1) + +#define OFW_CELL_TO_UINT64(cell) \ + (((uint64_t)(*(cell)) << 32) | (uint64_t)(*((cell) + 1))) + +typedef enum { + AL_PCI_TYPE_INTERNAL = 1, + AL_PCI_TYPE_EXTERNAL = 2, +} al_pci_type; + +static struct ofw_compat_data compat_data[] = { + {"annapurna-labs,al-internal-pcie", AL_PCI_TYPE_INTERNAL}, + {"annapurna-labs,alpine-internal-pcie", AL_PCI_TYPE_INTERNAL}, + {"annapurna-labs,al-external-pcie", AL_PCI_TYPE_EXTERNAL}, + {NULL, 0} +}; + +struct al_pci_range { + uintmax_t base_pci; + uintmax_t base_parent; + uintmax_t len; +}; + +typedef struct { + struct ofw_pci_softc sc_ofw; + device_t sc_dev; + al_pci_type sc_type; + uint32_t sc_busnr; + bus_addr_t ecam_handle; + struct resource *reg; + bus_space_tag_t reg_bst; + struct al_pcie_port pcie_port; + bus_addr_t reg_handle; + uint32_t index; + struct al_pcie_link_status status; + struct mtx conf_lock; + uint32_t target_bus; + struct al_pci_range pci_range[AL_PCI_RANGE_MAX]; + struct rman sc_mem_rman; + struct rman sc_io_rman; + uint32_t sc_io_map[AL_PCI_IO_SLICE_SIZE / + (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)]; + uint32_t sc_mem_map[AL_PCI_MEM_SLICE_SIZE / + (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)]; +} al_pcib_softc; + +/* Forward prototypes */ +static int al_pcib_probe(device_t); +static int al_pcib_attach(device_t); + +static struct resource *al_pcib_alloc_resource(device_t, device_t, int, + int *, rman_res_t, rman_res_t, rman_res_t, u_int); +static int al_pcib_release_resource(device_t, device_t, int, int, + struct resource *); +static int al_pcib_init(al_pcib_softc *, int, int); +static void al_pcib_enable(al_pcib_softc *); +static int al_pcib_maxslots(device_t); +static uint32_t al_pcib_read_config(device_t, u_int, u_int, u_int, u_int, + int); +static void al_pcib_write_config(device_t, u_int, u_int, u_int, u_int, + uint32_t, int); +static void al_pcib_fixup(al_pcib_softc *, int, int, int); +static int al_pcib_mem_init(al_pcib_softc *); +static uint32_t pcib_map_check(uint32_t *, uint32_t, uint32_t); +static void pcib_map_set(uint32_t *, uint32_t, uint32_t); +static bus_addr_t pcib_alloc(al_pcib_softc *, uint32_t); +static int al_pcib_init_bar(al_pcib_softc *, int, int, int, int); +static int al_pcib_init_all_bars(al_pcib_softc *, int, int, int, int); +static void al_pcib_init_bridge(al_pcib_softc *, int, int, int); + +static int pcie_set_mps(al_pcib_softc *, int, int, int, int); +static int pcie_get_mps(al_pcib_softc *, int, int, int); +static uint16_t get_pcie_max_payload(al_pcib_softc *, int, int, int); +static uint16_t get_pcie_get_type(al_pcib_softc *, int, int, int); +static void pcie_write_mps(al_pcib_softc *, int, int, int, int); +static uint16_t pcie_get_readrq(al_pcib_softc *, int, int, int); +static int pcie_set_readrq(al_pcib_softc *, int, int, int, int); +static void pcie_write_mrrs(al_pcib_softc *, int, int, int); +static int pcie_bus_configure_set(al_pcib_softc *, int, int, int, uint8_t *); +static int al_pcie_cfg_prepare(al_pcib_softc *); +static int al_pcie_cfg_addr(device_t, u_int, u_int, u_int, u_int, int, + bus_addr_t *, bus_addr_t *); +static int al_pcie_enable_controller(al_pcib_softc *); +static int al_pcie_port_check_link(al_pcib_softc *); +static int al_pcie_cfg_prepare(al_pcib_softc *); +static int al_pcie_io_prepare(al_pcib_softc *); + +static int al_find_cap(device_t, u_int, u_int, u_int, int, int *); +static int al_pci_ranges_decode(phandle_t, + struct al_pci_range *, struct al_pci_range *, + struct al_pci_range *, al_pcib_softc *); + +#ifdef ALPINE_PCI_MSI +static int al_pcib_map_msi(device_t, device_t, int, uint64_t *, uint32_t *); +static int al_pcib_alloc_msi(device_t, device_t, int, int, int *); +static int al_pcib_release_msi(device_t, device_t, int, int *); +#endif +#ifdef ALPINE_PCI_MSIX +#ifndef ALPINE_PCI_MSI +#error ALPINE_PCI_MSI must be defined prior enabling ALPINE_PCI_MSIX +#endif +static int al_pcib_alloc_msix(device_t, device_t, int *); +static int al_pcib_release_msix(device_t, device_t, int); +#endif + +/* + * Bus interface definitions. + */ +static device_method_t al_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, al_pcib_probe), + DEVMETHOD(device_attach, al_pcib_attach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar), + DEVMETHOD(bus_write_ivar, ofw_pci_write_ivar), + DEVMETHOD(bus_alloc_resource, al_pcib_alloc_resource), + DEVMETHOD(bus_release_resource, al_pcib_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, al_pcib_maxslots), + DEVMETHOD(pcib_read_config, al_pcib_read_config), + DEVMETHOD(pcib_write_config, al_pcib_write_config), + DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt), + +#ifdef ALPINE_PCI_MSI + DEVMETHOD(pcib_alloc_msi, al_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, al_pcib_release_msi), + DEVMETHOD(pcib_map_msi, al_pcib_map_msi), +#endif + +#ifdef ALPINE_PCI_MSIX + DEVMETHOD(pcib_alloc_msix, al_pcib_alloc_msix), + DEVMETHOD(pcib_release_msix, al_pcib_release_msix), +#endif + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static driver_t al_pcib_driver = { + "pcib", + al_pcib_methods, + sizeof(al_pcib_softc), +}; + +static devclass_t anpa_pcib_devclass; + +DRIVER_MODULE(anpa_pcib, simplebus, al_pcib_driver, anpa_pcib_devclass, 0, 0); +DRIVER_MODULE(anpa_pcib, ofwbus, al_pcib_driver, anpa_pcib_devclass, 0, 0); + +static int +al_pcib_probe(device_t dev) +{ + al_pci_type pcit; + const struct ofw_compat_data *compat = + ofw_bus_search_compatible(dev, compat_data); + + if (compat != NULL) { + pcit = compat->ocd_data; + switch (pcit) { + case AL_PCI_TYPE_INTERNAL: + device_set_desc(dev, + "Annapurna-Labs Integrated Internal PCI-E Controller"); + return (BUS_PROBE_DEFAULT); + case AL_PCI_TYPE_EXTERNAL: + device_set_desc(dev, + "Annapurna-Labs Integrated External PCI-E Controller"); + return (BUS_PROBE_DEFAULT); + default: + break; + } + } + + return (ENXIO); +} + +static int +al_pcib_attach(device_t dev) +{ + al_pcib_softc *sc; + phandle_t node; + uint64_t reg_base = 0; + uint64_t reg_size = 0; + int ret = 0; + int rid; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + sc->sc_dev = dev; + sc->sc_busnr = device_get_unit(dev); + sc->index = device_get_unit(dev); + sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + mtx_init(&sc->conf_lock, "ALPCICFG", NULL, MTX_DEF); + + rid = 0; + sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->reg == NULL) { + device_printf(dev, "Failed to allocate resource\n"); + return (ENXIO); + } + sc->reg_bst = rman_get_bustag(sc->reg); + sc->ecam_handle = rman_get_bushandle(sc->reg); + + /* Map PCI regions */ + ret = al_pci_ranges_decode(node, &sc->pci_range[AL_PCI_RANGE_BRIDGE], + &sc->pci_range[AL_PCI_RANGE_IO], &sc->pci_range[AL_PCI_RANGE_MEM], + sc); + + if (ret != 0) { + device_printf(dev, "Decode PCI ranges failed, ret=%d\n", ret); + return (EFAULT); + } + + if (sc->sc_type == AL_PCI_TYPE_EXTERNAL) + { + reg_base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci; + reg_size = sc->pci_range[AL_PCI_RANGE_MEM].len; + + if (reg_size > 0) + { + ret = bus_space_map(sc->reg_bst, (bus_addr_t)reg_base, + (bus_size_t)reg_size, 0, &sc->reg_handle); + if (ret != 0) { + device_printf(dev, "Failed to map register space\n"); + return (EFAULT); + } + } + } + + /* Get PCI interrupt info. */ + ofw_bus_setup_iinfo(node, &sc->sc_ofw.sc_pci_iinfo, sizeof(pcell_t)); + + if (bootverbose) { + device_printf(dev, + "bridge space: base_parent = 0x%jx, base_pci = 0x%jx " + "len = %jx\n", + sc->pci_range[AL_PCI_RANGE_BRIDGE].base_parent, + sc->pci_range[AL_PCI_RANGE_BRIDGE].base_pci, + sc->pci_range[AL_PCI_RANGE_BRIDGE].len); + device_printf(dev, + "memory space: base_parent = 0x%jx, base_pci = 0x%jx " + "len = %jx\n", sc->pci_range[AL_PCI_RANGE_MEM].base_parent, + sc->pci_range[AL_PCI_RANGE_MEM].base_pci, + sc->pci_range[AL_PCI_RANGE_MEM].len); + device_printf(dev, + "IO space: base_parent = 0x%jx, base_pci = 0x%jx " + "len = %jx\n", sc->pci_range[AL_PCI_RANGE_IO].base_parent, + sc->pci_range[AL_PCI_RANGE_IO].base_pci, + sc->pci_range[AL_PCI_RANGE_IO].len); + } + + if (sc->pci_range[AL_PCI_RANGE_BRIDGE].len != 0) { + if (bus_space_map(sc->reg_bst, + sc->pci_range[AL_PCI_RANGE_BRIDGE].base_parent, + sc->pci_range[AL_PCI_RANGE_BRIDGE].len, 0, &sc->ecam_handle)) { + device_printf(dev, "Failed to map bridge bus address\n"); + return (EFAULT); + } + } + + if (bootverbose) + device_printf(dev, "ecam_baddr = 0x%p\n", + (void*)sc->ecam_handle); + + ret = al_pcie_enable_controller(sc); + if (ret != 0) { + device_printf(dev, "Failed to enable PCIE controller\n"); + return (EFAULT); + } + + ret = al_pcie_port_check_link(sc); + if (ret == 0) { + device_printf(dev, "Failed to check link\n"); + return (EFAULT); + } + + ret = al_pcie_cfg_prepare(sc); + if (ret != 0) { + device_printf(dev, "Failed to prepare config\n"); + return (EFAULT); + } + + ret = al_pcie_io_prepare(sc); + if (ret != 0) { + device_printf(dev, "Failed to prepare IO\n"); + return (EFAULT); + } + + /* Configure IOCC for external PCIE */ + if (sc->sc_type != AL_PCI_TYPE_INTERNAL) { + al_pcie_port_snoop_config(&sc->pcie_port, 1); + al_pcib_enable(sc); + } + + ret = al_pcib_mem_init(sc); + if (ret != 0) { + device_printf(dev, "Failed to al_pcib_mem_init\n"); + return (EFAULT); + } + + ret = al_pcib_init(sc, sc->sc_busnr, + al_pcib_maxslots(sc->sc_dev)); + if (ret != 0) + return ret; + + device_add_child(dev, "pci", -1); + + return (bus_generic_attach(dev)); +} + +static int +al_pci_ranges_decode(phandle_t node, struct al_pci_range *bridge_space, + struct al_pci_range *io_space, struct al_pci_range *mem_space, + al_pcib_softc *sc) +{ + pcell_t *ranges; + struct al_pci_range *pci_space; + struct ofw_pci_cell_info *cell_info; + pcell_t *rangesptr; + pcell_t cell0; + int i, rv, offset_cells; + int tuples, bytelen; + + cell_info = (struct ofw_pci_cell_info *)malloc(sizeof(*cell_info), + M_DEVBUF, M_WAITOK | M_ZERO); + + sc->sc_ofw.sc_cell_info = cell_info; + tuples = ofw_pci_nranges(node, cell_info); + if (tuples <= 0) { + rv = ERANGE; + goto out1; + } + + bytelen = OF_getproplen(node, "ranges"); + if (bytelen <= 0) { + rv = ERANGE; + goto out1; + } + + ranges = (pcell_t *)malloc(bytelen, M_DEVBUF, M_WAITOK); + if (OF_getencprop(node, "ranges", ranges, bytelen) == -1) { + device_printf(sc->sc_dev, "Error parsing FDT 'ranges' property\n"); + rv = ERANGE; + goto out2; + } + + /* + * Initialize the ranges so that we don't have to worry about + * having them all defined in the FDT. In particular, it is + * perfectly fine not to want I/O space on PCI busses. + */ + bzero(io_space, sizeof(*io_space)); + bzero(mem_space, sizeof(*mem_space)); + bzero(bridge_space, sizeof(*bridge_space)); + + rangesptr = &ranges[0]; + offset_cells = 0; + + for (i = 0; i < tuples; i++) { + cell0 = *rangesptr++; + + if ((cell0 & FDT_RANGE_MEM) != 0) { + pci_space = mem_space; + } else if ((cell0 & FDT_RANGE_IO) != 0) { + pci_space = io_space; + } else if (cell0 == 0) { + pci_space = bridge_space; + } else { + rv = ERANGE; + goto out2; + } + + pci_space->base_pci = OFW_CELL_TO_UINT64(rangesptr); + rangesptr += 2; + + if (cell_info->host_address_cells == 3) { + /* + * This is a PCI subnode 'ranges'. Skip cell0 and + * cell1 of this entry and only use cell2. + */ + offset_cells = 2; + rangesptr += offset_cells; + } + + if ((cell_info->host_address_cells - offset_cells) == 1) + pci_space->base_parent = *rangesptr; + else + pci_space->base_parent = OFW_CELL_TO_UINT64(rangesptr); + + rangesptr += cell_info->host_address_cells - offset_cells; + + if (cell_info->size_cells == 1) + pci_space->len = *rangesptr; + else + pci_space->len = OFW_CELL_TO_UINT64(rangesptr); + + rangesptr += cell_info->size_cells; + } + + rv = 0; +out2: + free(ranges, M_DEVBUF); +out1: + free(cell_info, M_DEVBUF); + return (rv); +} + +static int +al_pcie_enable_controller(al_pcib_softc *sc) +{ + + if (sc->sc_type == AL_PCI_TYPE_INTERNAL) + return (0); + + /* Initialize PCIe HAL with register tag and handle */ + al_pcie_port_handle_init(&sc->pcie_port, + al_bus_dma_to_va(sc->reg_bst, + sc->reg_handle), NULL /* fixme */, sc->index); + if (al_pcie_operating_mode_get(&sc->pcie_port) != + AL_PCIE_OPERATING_MODE_RC) { + device_printf(sc->sc_dev, + "controller is not configured to Root-Complex mode\n"); + return (ENOSYS); + } + + return (0); +} + +/* Get ECAM address according to bus, device, function, and offset */ +static int +al_pcie_cfg_addr(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, + int bytes, bus_addr_t *handle, bus_addr_t *addr) +{ + al_pcib_softc *sc = device_get_softc(dev); + bus_addr_t ret_val = (bus_addr_t)NULL; + + /* Trap out illegal values */ + if (bus > PCI_MAX_BUS_NUMBER) + return (EFAULT); + + ret_val = (bus_addr_t)(((slot << AL_AXI_SLOT) | + (func << AL_AXI_FUNC) | reg)); + if (sc->sc_type == AL_PCI_TYPE_INTERNAL) { + *addr = ret_val; + *handle = sc->ecam_handle; + return (0); + } + + /* Normalize bus number to the base of parent bridge */ + bus -= sc->sc_busnr; + + /* If there is no link, just show the PCI bridge. */ + if (sc->status.link_up == AL_FALSE) + return (EFAULT); + + if (slot > 0) + return (EFAULT); + + if (bus == 0) { + ret_val = (bus_addr_t)(AL_INTERNAL_BRIDGE_ECAM_BASE + reg); + *handle = sc->reg_handle; + *addr = ret_val; + return (0); + } + /* + * Workaround: it enumerates on all internal bus numbers so + * communicate only with the first device on the bus + */ + if (bus != sc->target_bus) { + if (bootverbose) + device_printf(sc->sc_dev, + "change target bus number from %d to %d\n", + sc->target_bus, bus); + sc->target_bus = bus; + al_pcie_target_bus_set(&sc->pcie_port, sc->target_bus, TARGET_BUS_MASK); + } + + *addr = ret_val; + *handle = sc->ecam_handle; + return (0); +} + +static int +al_pcie_port_check_link(al_pcib_softc *sc) +{ + struct al_pcie_link_status *status = &sc->status; + int rc; + + if (sc->sc_type == AL_PCI_TYPE_INTERNAL) + return (1); + + rc = al_pcie_link_status(&sc->pcie_port, status); + + if (status->link_up == AL_FALSE) { + device_printf(sc->sc_dev, "link %u down\n", sc->index); + return (0); + } + device_printf(sc->sc_dev, "link up: speed Gen %d width x%x\n", + status->speed, status->lanes); + + return (1); +} + +/* prepare controller for issuing CFG transactions*/ +static int +al_pcie_cfg_prepare(al_pcib_softc *sc) +{ + + if (sc->sc_type == AL_PCI_TYPE_INTERNAL) + return (0); + + sc->target_bus = 1; + /* + * force the controller to set the pci bus in the TLP to + * pcie->target_bus no matter what is the bus portion of the ECAM addess + * is. + */ + al_pcie_target_bus_set(&sc->pcie_port, sc->target_bus, TARGET_BUS_MASK); + + /* the bus connected to the controller always enumerated as bus 1*/ + al_pcie_secondary_bus_set(&sc->pcie_port, 1); + /* set subordinary to max value */ + al_pcie_subordinary_bus_set(&sc->pcie_port, MAX_SUBORDINARY_BUS); + + return (0); +} + +/* prepare controller for issuing IO transactions*/ +static int +al_pcie_io_prepare(al_pcib_softc *sc) +{ + struct al_pcie_atu_region io_atu_region; + + if (sc->sc_type == AL_PCI_TYPE_INTERNAL) + return (0); + + io_atu_region.enable = AL_TRUE; + io_atu_region.direction = AL_PCIE_ATU_DIR_OUTBOUND; + io_atu_region.index = 0; + io_atu_region.base_addr = sc->pci_range[AL_PCI_RANGE_IO].base_parent; + io_atu_region.limit = sc->pci_range[AL_PCI_RANGE_IO].base_parent + + sc->pci_range[AL_PCI_RANGE_IO].len - 1; + /* the address that matches will be translated to this address + offset */ + io_atu_region.target_addr = sc->pci_range[AL_PCI_RANGE_IO].base_pci; + io_atu_region.invert_matching = AL_FALSE; + io_atu_region.tlp_type = AL_PCIE_TLP_TYPE_IO; /* pcie tlp type*/ + io_atu_region.attr = 0; /* pcie frame header attr field*/ + /* outbound specific params */ + io_atu_region.msg_code = 0; /* pcie message code */ + io_atu_region.cfg_shift_mode = AL_FALSE; + /* inbound specific params*/ + if (bootverbose) { + device_printf(sc->sc_dev, + "%s: base %jx, limit %jx, target %jx\n", + __func__, (uintmax_t)io_atu_region.base_addr, + (uintmax_t)io_atu_region.limit, + (uintmax_t)io_atu_region.target_addr); + } + al_pcie_atu_region_set(&sc->pcie_port, &io_atu_region); + + return (0); +} + +static int +al_pcib_mem_init(al_pcib_softc *sc) +{ + int err; + + /* + * Memory management. + */ + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + err = rman_init(&sc->sc_mem_rman); + if (err != 0) + return (err); + + sc->sc_io_rman.rm_type = RMAN_ARRAY; + err = rman_init(&sc->sc_io_rman); + if (err != 0) { + rman_fini(&sc->sc_mem_rman); + return (err); + } + + err = rman_manage_region(&sc->sc_mem_rman, + sc->pci_range[AL_PCI_RANGE_MEM].base_pci, + sc->pci_range[AL_PCI_RANGE_MEM].base_pci + + sc->pci_range[AL_PCI_RANGE_MEM].len - 1); + if (err != 0) + goto error; + + err = rman_manage_region(&sc->sc_io_rman, + sc->pci_range[AL_PCI_RANGE_IO].base_pci, + sc->pci_range[AL_PCI_RANGE_IO].base_pci + + sc->pci_range[AL_PCI_RANGE_IO].len - 1); + if (err != 0) + goto error; + + return (0); + +error: + rman_fini(&sc->sc_mem_rman); + rman_fini(&sc->sc_io_rman); + + return (err); +} + +static inline uint32_t +pcib_map_check(uint32_t *map, uint32_t start, uint32_t bits) +{ + uint32_t i; + + for (i = start; i < start + bits; i++) { + if (isset(map, i)) + return (0); + } + + return (1); +} + +static inline void +pcib_map_set(uint32_t *map, uint32_t start, uint32_t bits) +{ + uint32_t i; + + for (i = start; i < start + bits; i++) + setbit(map, i); +} + +static bus_addr_t +pcib_alloc(al_pcib_softc *sc, uint32_t smask) +{ + uint32_t bits, bits_limit, i, *map, min_alloc, size; + bus_addr_t addr = 0; + bus_addr_t base; + + if (smask & 1) { + base = sc->pci_range[AL_PCI_RANGE_IO].base_pci; + min_alloc = PCI_MIN_IO_ALLOC; + bits_limit = sc->pci_range[AL_PCI_RANGE_IO].len / min_alloc; + map = sc->sc_io_map; + smask &= ~0x3; + } else { + base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci; + min_alloc = PCI_MIN_MEM_ALLOC; + bits_limit = sc->pci_range[AL_PCI_RANGE_MEM].len / min_alloc; + map = sc->sc_mem_map; + smask &= ~0xF; + } + + size = ~smask + 1; + bits = size / min_alloc; + + for (i = 0; i + bits <= bits_limit; i += bits) { + if (pcib_map_check(map, i, bits)) { + pcib_map_set(map, i, bits); + addr = base + (i * min_alloc); + goto end; + } + } + +end: + return (addr); +} + +static int +al_pcib_init_bar(al_pcib_softc *sc, int bus, int slot, int func, + int barno) +{ + uint32_t addr, bar; + int reg, width; + + reg = PCIR_BAR(barno); + + /* + * Need to init the BAR register with 0xffffffff before correct + * value can be read. + */ + al_pcib_write_config(sc->sc_dev, bus, slot, func, reg, 0xfffffffful, 4); + bar = al_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4); + if (bar == 0) + return (1); + + /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */ + width = BAR_SIZE_IN_32BIT_UNITS(bar); + + addr = pcib_alloc(sc, bar); + if (addr == 0) + return (-1); + + if (bootverbose) + printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n", + bus, slot, func, reg, bar, addr); + + al_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4); + if (width == 2) + al_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4, + 0, 4); + + return (width); +} + +static int +al_pcib_init_all_bars(al_pcib_softc *sc, int bus, int slot, + int func, int hdrtype) +{ + int maxbar, bar, i; + + maxbar = (hdrtype & PCIM_HDRTYPE) != 0 ? 0 : 6; + bar = 0; + + /* Program the base address registers */ + while (bar < maxbar) { + i = al_pcib_init_bar(sc, bus, slot, func, bar); + bar += i; + if (i < 0) { + device_printf(sc->sc_dev, + "PCI IO/Memory space exhausted\n"); + return (ENOMEM); + } + } + + return (0); +} + +static void +al_pcib_init_bridge(al_pcib_softc *sc, int bus, int slot, int func) +{ + bus_addr_t io_base, mem_base; + bus_size_t io_limit, mem_limit; + int secbus; + + io_base = sc->pci_range[AL_PCI_RANGE_IO].base_pci; + io_limit = io_base + sc->pci_range[AL_PCI_RANGE_IO].len - 1; + mem_base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci; + mem_limit = mem_base + sc->pci_range[AL_PCI_RANGE_MEM].len - 1; + + /* Configure I/O decode registers */ + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1, + io_base >> 8, 1); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1, + io_base >> 16, 2); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1, + io_limit >> 8, 1); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1, + io_limit >> 16, 2); + + /* Configure memory decode registers */ + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1, + mem_base >> 16, 2); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1, + mem_limit >> 16, 2); + + /* Disable memory prefetch decode */ + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1, + 0x10, 2); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1, + 0x0, 4); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1, + 0xF, 2); + al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1, + 0x0, 4); + + secbus = al_pcib_read_config(sc->sc_dev, bus, slot, func, + PCIR_SECBUS_1, 1); + + /* Configure buses behind the bridge */ + al_pcib_init(sc, secbus, PCI_SLOTMAX); +} + +static int +al_find_cap(device_t child, u_int bus, u_int slot, u_int func, + int capability, int *capreg) +{ + uint32_t status; + uint8_t ptr; + uint16_t hdrtype; + + /* + * Check the CAP_LIST bit of the PCI status register first. + */ + status = al_pcib_read_config(child, bus, slot, func, PCIR_STATUS, 2); + if ((status & PCIM_STATUS_CAPPRESENT) != PCIM_STATUS_CAPPRESENT) + return (ENXIO); + + hdrtype = al_pcib_read_config(child, bus, slot, func, PCIR_HDRTYPE, 1); + + /* + * Determine the start pointer of the capabilities list. + */ + switch (hdrtype & PCIM_HDRTYPE) { + case PCIM_HDRTYPE_NORMAL: + case PCIM_HDRTYPE_BRIDGE: + ptr = PCIR_CAP_PTR; + break; + case PCIM_HDRTYPE_CARDBUS: + ptr = PCIR_CAP_PTR_2; + break; + default: + device_printf(child, "al_find_cap: invalid hdrtype %x\n", + hdrtype & PCIM_HDRTYPE); + return (ENXIO); /* no extended capabilities support */ + } + ptr = al_pcib_read_config(child, bus, slot, func, ptr, 1); + + /* + * Traverse the capabilities list. + */ + while (ptr != 0) { + if (al_pcib_read_config(child, bus, slot, func, + ptr + PCICAP_ID, 1) == capability) { + if (capreg != NULL) + *capreg = ptr; + return (0); + } + ptr = al_pcib_read_config(child, bus, slot, func, + ptr + PCICAP_NEXTPTR, 1); + } + + return (ENOENT); +} + +/** + * pcie_get_mps - get PCI Express maximum payload size configured + * @dev: PCI device to query + * + * Returns maximum payload size in bytes + * or appropriate error value. + */ +static int +pcie_get_mps(al_pcib_softc *sc, int bus, int slot, int func) +{ + uint16_t ctl; + int reg; + + if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0) { + device_printf(sc->sc_dev, "Failed to find PCIY_EXPRESS cap\n"); + return (0); + } + + ctl = al_pcib_read_config(sc->sc_dev, bus, slot, func, + reg + PCIER_DEVICE_CTL, 2); + + return (PCIE_MIN_MPS << ((ctl & PCIEM_CTL_MAX_PAYLOAD) >> + PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS)); +} + +/** + * get_pcie_max_payload - get PCI Express maximum supported payload size + * @dev: PCI device to query + * + * Returns maximum payload size in bytes + * or appropriate error value. + */ +static uint16_t +get_pcie_max_payload(al_pcib_softc *sc, int bus, int slot, int func) +{ + uint16_t reg16; + int reg; + + if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0) + return (0); + + reg16 = al_pcib_read_config(sc->sc_dev, bus, slot, + func, reg + PCIER_DEVICE_CAP, 2); + return (reg16 & PCIEM_CAP_MAX_PAYLOAD); +} + +/** + * get_pcie_get_type - get PCI Express device type (device or bridge) + * @dev: PCI device to query + * + * Returns maximum payload size in byte, rids + * or appropriate error value. + */ +static uint16_t +get_pcie_get_type(al_pcib_softc *sc, int bus, int slot, int func) +{ + uint16_t reg16; + int reg; + + if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0) + return (0); + + reg16 = al_pcib_read_config(sc->sc_dev, bus, slot, + func, reg + PCIER_FLAGS, 2); + return (reg16 & PCIEM_FLAGS_TYPE); +} + +/** + * pcie_set_mps - set PCI Express maximum payload size + * @dev: PCI device to query + * @mps: maximum payload size in bytes + * valid values are 128, 256, 512, 1024, 2048, 4096 + * + * If possible sets maximum payload size + */ +static int +pcie_set_mps(al_pcib_softc *sc, int mps, int bus, int slot, int func) +{ + uint16_t v, tmp; + int reg; + + if (mps < PCIE_MIN_MPS || mps > PCIE_MAX_MPS || + powerof2(mps) == 0) { + return (EFAULT); + } + + v = ffs(mps) - 8; + if (v > get_pcie_max_payload(sc, bus, slot, func)) + return (EFAULT); + v <<= PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS; + + if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0) + return (EFAULT); + + tmp = al_pcib_read_config(sc->sc_dev, bus, slot, func, + reg + PCIER_DEVICE_CTL, 2); + tmp &= ~PCIEM_CTL_MAX_PAYLOAD; + tmp |= (v & PCIEM_CTL_MAX_PAYLOAD); + al_pcib_write_config(sc->sc_dev, bus, slot, func, + reg + PCIER_DEVICE_CTL, tmp, 2); + + return (0); +} + +static void +pcie_write_mps(al_pcib_softc *sc, int mps, int bus, int slot, int func) +{ + int rc; + device_t parent; + uint16_t type; + + mps = PCIE_MIN_MPS << get_pcie_max_payload(sc, bus, slot, func); + + parent = device_get_parent(sc->sc_dev); + type = get_pcie_get_type(sc, bus, slot, func); + + mps = min(mps, AL_MAX_SUPPORTED_MPS); + + rc = pcie_set_mps(sc, mps, bus, slot, func); + if (rc != 0) { + device_printf(sc->sc_dev, + "Failed attempting to set the MPS %d\n", mps); + } +} + +static uint16_t +pcie_get_readrq(al_pcib_softc *sc, int bus, int slot, int func) +{ + uint16_t ctl; + int reg; + + if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0) + return (0); + + ctl = al_pcib_read_config(sc->sc_dev, bus, slot, func, + reg + PCIER_DEVICE_CTL, 2); + + return (PCIE_MIN_MRRS << ((ctl & PCIEM_CTL_MAX_READ_REQUEST) >> + PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS)); +} + +/** + * pcie_set_readrq - set PCI Express maximum memory read request + * @dev: PCI device to query + * @rq: maximum memory read count in bytes + * valid values are 128, 256, 512, 1024, 2048, 4096 + * + * If possible sets maximum memory read request in bytes + */ +static int +pcie_set_readrq(al_pcib_softc *sc, int rq, int bus, int slot, int func) +{ + uint16_t v, tmp; + int mps, reg; + + if (rq < PCIE_MIN_MRRS || rq > PCIE_MAX_MRRS || powerof2(rq) == 0) { + return (EINVAL); + } + + mps = pcie_get_mps(sc, bus, slot, func); + + if (mps < 0) + return (mps); + if (mps < rq) + rq = mps; + + v = (ffs(rq) - 8) << PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS; + + if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0) + return (EFAULT); + + tmp = al_pcib_read_config(sc->sc_dev, bus, slot, + func, reg + PCIER_DEVICE_CTL, 2); + tmp &= ~PCIEM_CTL_MAX_READ_REQUEST; + tmp |= (v & PCIEM_CTL_MAX_READ_REQUEST); + al_pcib_write_config(sc->sc_dev, bus, slot, + func, reg + PCIER_DEVICE_CTL, tmp, 2); + + return (0); +} + +static void +pcie_write_mrrs(al_pcib_softc *sc, int bus, int slot, int func) +{ + int rc, mrrs; + + /* For Max performance, the MRRS must be set to the largest supported + * value. However, it cannot be configured larger than the MPS the + * device or the bus can support. This should already be properly + * configured by a prior call to pcie_write_mps. + */ + mrrs = pcie_get_mps(sc, bus, slot, func); + + /* MRRS is a R/W register. Invalid values can be written, but a + * subsequent read will verify if the value is acceptable or not. + * If the MRRS value provided is not acceptable (e.g., too large), + * shrink the value until it is acceptable to the HW. + */ + while (mrrs != pcie_get_readrq(sc, bus, slot, func) && mrrs >= + PCIE_MIN_MRRS) { + rc = pcie_set_readrq(sc, mrrs, bus, slot, func); + if (rc == 0) + break; + + device_printf(sc->sc_dev, + "Failed attempting to set the MRRS\n"); + mrrs /= 2; + } + + if (mrrs < PCIE_MIN_MRRS) + device_printf(sc->sc_dev, + "MRRS was unable to be configured with a " + "safe value. If problems are experienced, try running " + "with pci=pcie_bus_safe.\n"); +} + +static int +pcie_bus_configure_set(al_pcib_softc *sc, int bus, int slot, int func, + uint8_t *data) +{ + int mps, orig_mps; + + mps = PCIE_MIN_MPS << *(uint8_t *)data; + + orig_mps = pcie_get_mps(sc, bus, slot, func); + + pcie_write_mps(sc, mps, bus, slot, func); + pcie_write_mrrs(sc, bus, slot, func); + + device_printf(sc->sc_dev, + "PCI-E Max Payload Size set to %4d/%4d (was %4d), " + "Max Read Rq %4d\n", pcie_get_mps(sc, bus, slot, func), + PCIE_MIN_MPS << get_pcie_max_payload(sc, bus, slot, func), + orig_mps, pcie_get_readrq(sc, bus, slot, func)); + + return (0); +} + +static void +al_pcib_fixup(al_pcib_softc *sc, int bus, int slot, int func) +{ + uint32_t val; + uint8_t smpss = 0; + pcie_bus_configure_set(sc, bus, slot, func, &smpss); + + /* Do not run fixups on external PCIe bus */ + if (bus != 0) + return; + + val = al_pcib_read_config(sc->sc_dev, bus, slot, func, + AL_PCI_AXI_CFG_AND_CTR_0, 4); + val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_2_PF_VEC_PH_VEC_OVRD_FROM_AXUSER_MASK; + al_pcib_write_config(sc->sc_dev, bus, slot, func, + AL_PCI_AXI_CFG_AND_CTR_0, val, 4); + + val = al_pcib_read_config(sc->sc_dev, bus, slot, func, + AL_PCI_APP_CONTROL, 4); + val &= ~0xffff; + val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_4_PF_VEC_MEM_ADDR54_63_SEL_VMID_MASK; + al_pcib_write_config(sc->sc_dev, bus, slot, func, + AL_PCI_APP_CONTROL, val, 4); +} + +static int +al_pcib_init(al_pcib_softc *sc, int bus, int maxslot) +{ + int slot, func, maxfunc, error, buscnt; + uint8_t hdrtype, command, class, subclass; + + buscnt = sc->sc_busnr; + + for (slot = 0; slot <= maxslot; slot++) { + maxfunc = 0; + for (func = 0; func <= maxfunc; func++) { + hdrtype = al_pcib_read_config(sc->sc_dev, bus, slot, + func, PCIR_HDRTYPE, 1); + + if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) + continue; + + if (func == 0 && (hdrtype & PCIM_MFDEV) != 0) + maxfunc = PCI_FUNCMAX; + + command = al_pcib_read_config(sc->sc_dev, bus, slot, + func, PCIR_COMMAND, 1); + command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); + al_pcib_write_config(sc->sc_dev, bus, slot, func, + PCIR_COMMAND, command, 1); + + error = al_pcib_init_all_bars(sc, bus, slot, func, + hdrtype); + + if (error) + return (error); + + command |= PCIM_CMD_PORTEN; + al_pcib_write_config(sc->sc_dev, bus, slot, func, + PCIR_COMMAND, command, 1); + + /* Handle PCI-PCI bridges */ + class = al_pcib_read_config(sc->sc_dev, bus, slot, + func, PCIR_CLASS, 1); + subclass = al_pcib_read_config(sc->sc_dev, bus, slot, + func, PCIR_SUBCLASS, 1); + + al_pcib_fixup(sc, bus, slot, func); + + if (class != PCIC_BRIDGE || + subclass != PCIS_BRIDGE_PCI) { + continue; + } + + /* Write appropriate bus number */ + buscnt++; + al_pcib_write_config(sc->sc_dev, bus,0,0, + PCIR_SECBUS_1, buscnt, 1); + al_pcib_write_config(sc->sc_dev, bus,0,0, + PCIR_SUBBUS_1, buscnt, 1); + + al_pcib_init_bridge(sc, bus, slot, func); + } + } + + return (0); +} + +static void +al_pcib_enable(al_pcib_softc *sc) +{ + uint32_t val; + + /* + * Enable PCI bridge. + */ + val = al_pcib_read_config(sc->sc_dev, sc->sc_busnr, 0, 0, + PCIR_COMMAND, 4); + val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | + PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; + al_pcib_write_config(sc->sc_dev, sc->sc_busnr, 0, 0, + PCIR_COMMAND, val, 4); +} + +static struct resource * +al_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + al_pcib_softc *sc = device_get_softc(dev); + struct rman *rm = NULL; + struct resource *res; + rman_res_t bus_start = 0, bus_size = 0; + + if (bootverbose) + device_printf(dev, "al_pcib_alloc_resource begin, start=0x%jx, " + "end=0x%jx, count=0x%jx, type=%d\n", + (uintmax_t)start, (uintmax_t)end, (uintmax_t)count, type); + + switch (type) { + case SYS_RES_IOPORT: + rm = &sc->sc_io_rman; + bus_start = sc->pci_range[AL_PCI_RANGE_IO].base_pci; + bus_size = sc->pci_range[AL_PCI_RANGE_IO].len; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + bus_start = sc->pci_range[AL_PCI_RANGE_MEM].base_pci; + bus_size = sc->pci_range[AL_PCI_RANGE_MEM].len; + break; + default: + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, + type, rid, start, end, count, flags)); + }; + + if ((start == 0UL) && (end == ~0UL)) { + start = bus_start; + end = bus_start + bus_size - 1; + count = bus_size; + } + + if ((start < bus_start) || (start + count - 1 != end) || + (end > bus_start + bus_size - 1)) { + device_printf(dev, "range failed\n"); + return (NULL); + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (res == NULL) { + device_printf(dev, "rman_reserve_resource failed\n"); + return (NULL); + } + + rman_set_rid(res, *rid); + + if ((flags & RF_ACTIVE) != 0) + if (bus_activate_resource(child, type, *rid, res)) { + rman_release_resource(res); + device_printf(dev, "bus_activate_resource failed\n"); + return (NULL); + } + + return (res); +} + +static int +al_pcib_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + + switch (type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + device_printf(dev, "Releasing SYS_RES_IOPORT and SYS_RES_MEMORY " + "resources is currently not supported. " + "This leads to memory leaks.\n"); + break; + default: + return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, + type, rid, res)); + }; + + return (0); +} + +static int +al_pcib_maxslots(device_t dev) +{ + + return (AL_PCI_MAX_SLOTS); +} + +static uint32_t +al_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + bus_addr_t addr; + uint32_t val = 0xffffffff; + al_pcib_softc *sc = device_get_softc(dev); + bus_addr_t handle = 0; + int ret; + + mtx_lock(&sc->conf_lock); + + ret = al_pcie_cfg_addr(dev, bus, slot, func, + reg & ~CONFIG_ADDR_ZERO_BITS_MASK, bytes, &handle, &addr); + + if (ret != 0) + goto end; + + val = bus_space_read_4(sc->reg_bst, handle, addr); + + switch (bytes) { + case 1: + val = (val >> + ((reg & CONFIG_ADDR_BYTE_SELECTOR_MASK) * NBBY)) & 0xff; + break; + case 2: + val = (val >> + ((reg & CONFIG_ADDR_BYTE_SELECTOR_MASK) * NBBY)) & 0xffff; + break; + default: + break; + } + +end: + mtx_unlock(&sc->conf_lock); + + return (val); +} + +static void +al_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int bytes) +{ + bus_addr_t addr; + al_pcib_softc *sc = device_get_softc(dev); + bus_addr_t handle = 0; + int ret; + + mtx_lock(&sc->conf_lock); + + ret = al_pcie_cfg_addr(dev, bus, slot, func, reg, + bytes, &handle, &addr); + if (ret != 0) + goto end; + + switch (bytes) { + case 1: + bus_space_write_1(sc->reg_bst, handle, addr, + (uint8_t)val); + break; + case 2: + bus_space_write_2(sc->reg_bst, handle, addr, + (uint16_t)val); + break; + case 4: + bus_space_write_4(sc->reg_bst, handle, addr, + (uint32_t)val); + break; + default: + break; + } + +end: + mtx_unlock(&sc->conf_lock); + + return; +} + +#ifdef ALPINE_PCI_MSI + +static int +al_pcib_map_msi(device_t dev __unused, device_t child __unused, int irq, + uint64_t *addr, uint32_t *data) +{ + + return (al_msix_map_msi(irq, addr, data)); +} + +static int +al_pcib_alloc_msi(device_t dev __unused, device_t child __unused, int count, + int maxcount __unused, int *irqs) +{ + + return (al_msix_alloc_msi(count, irqs)); +} + +static int +al_pcib_release_msi(device_t dev __unused, device_t child __unused, + int count, int *irqs) +{ + + return (al_msix_release_msi(count, irqs)); +} +#endif + +#ifdef ALPINE_PCI_MSIX +static int +al_pcib_alloc_msix(device_t dev __unused, device_t child __unused, int *irq) +{ + + return (al_msix_alloc_msi(1, irq)); +} + +static int +al_pcib_release_msix(device_t dev __unused, device_t child __unused, int irq) +{ + + return (al_msix_release_msi(1, &irq)); +} +#endif Index: sys/arm/annapurna/alpine/alpine_pci_msix.c =================================================================== --- /dev/null +++ sys/arm/annapurna/alpine/alpine_pci_msix.c @@ -0,0 +1,561 @@ +/*- +******************************************************************************* +Copyright (C) 2015,2016 Annapurna Labs Ltd. + +This file may be licensed under the terms of the Annapurna Labs Commercial +License Agreement. + +Alternatively, this file can be distributed under the terms of the GNU General +Public License V2 as published by the Free Software Foundation and can be +found at http://www.gnu.org/licenses/gpl-2.0.html + +Alternatively, redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include "alpine_pci.h" +#include "opt_alpine.h" + +#ifdef ARM_INTRNG +#include "pic_if.h" + +#define AL_SPI_INTR 0 +#define AL_LOW_TO_HIGH_TRIG 1 + +#define ERR_NOT_IN_MAP -1 + +#define IRQ_OFFSET 1 +#endif + +#define INTR_RANGE_COUNT 2 +#define SPI_OFFSET 32 +#define MAX_MSIX_COUNT 128 + +#define OFW_CELL_TO_UINT64(cell) \ + (((uint64_t)(*(cell)) << 32) | (uint64_t)(*((cell) + 1))) + +static int al_msix_attach(device_t); +static int al_msix_probe(device_t); +int al_msix_map_msi(int, uint64_t *, uint32_t *); +int al_msix_alloc_msi(int, int *); +int al_msix_release_msi(int, int *); + +#ifdef ARM_INTRNG +extern u_int intr_fdt_map_irq(phandle_t, pcell_t *, u_int); + +static int +al_msix_ic_register(device_t, struct intr_irqsrc *, boolean_t *); +static void +al_msix_ic_enable_intr(device_t, struct intr_irqsrc *); +static void +al_msix_ic_enable_source(device_t, struct intr_irqsrc *); +static int +al_msix_ic_unregister(device_t, struct intr_irqsrc *); +static void +al_msix_ic_disable_source(device_t, struct intr_irqsrc *); +static int +al_find_uintr_pos_in_map(int); + +struct al_intr_map { + u_int uintr; /* system interrupt number (from INTRNG framework) */ + u_int irq; /* hardware interrupt number (from DTB) */ +}; +#endif + +static struct ofw_compat_data compat_data[] = { + {"annapurna-labs,al-msix", true}, + {"annapurna-labs,alpine-msix", true}, + {NULL, false} +}; + +/* + * Bus interface definitions. + */ +static device_method_t al_msix_methods[] = { + DEVMETHOD(device_probe, al_msix_probe), + DEVMETHOD(device_attach, al_msix_attach), + +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_source, al_msix_ic_disable_source), + DEVMETHOD(pic_enable_intr, al_msix_ic_enable_intr), + DEVMETHOD(pic_enable_source, al_msix_ic_enable_source), + DEVMETHOD(pic_register, al_msix_ic_register), + DEVMETHOD(pic_unregister, al_msix_ic_unregister), +#endif + + DEVMETHOD_END +}; + +struct al_msix_softc { + device_t sc_dev; + bus_addr_t base_addr; + struct resource *reg; + uint32_t irq_min; + uint32_t irq_max; + uint32_t irq_count; + struct mtx msi_mtx; + char msi_bitmap[(MAX_MSIX_COUNT-1)/sizeof(char) + 1]; +#ifdef ARM_INTRNG + device_t gic_dev; + struct al_intr_map intr_map[MAX_MSIX_COUNT]; +#endif +}; + +static driver_t al_msix_driver = { + "al_msix", + al_msix_methods, + sizeof(struct al_msix_softc), +}; + +devclass_t al_msix_devclass; + +struct al_msix_softc *g_softc; + +DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, al_msix_devclass, 0, 0); +DRIVER_MODULE(al_msix, simplebus, al_msix_driver, al_msix_devclass, 0, 0); + +static int +al_msix_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Annapurna-Labs MSI-X Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +al_msix_attach(device_t dev) +{ + struct al_msix_softc *sc; + phandle_t node; + uint64_t reg_base = 0; + uint64_t reg_size = 0; + int rid; + pcell_t reg[4]; + pcell_t par_addr, par_size; +#ifdef ARM_INTRNG + int nintr, i, vec; + intptr_t xref; + uint32_t icells, *intr; + phandle_t iparent; + device_t gic_dev; + pcell_t cells[3] = {AL_SPI_INTR, + 0, /* This value will be overwritten + by actual SPI IRQ number */ + AL_LOW_TO_HIGH_TRIG}; +#else + struct resource_list rl; + struct resource_list_entry *rle; + int interrupts_count = 0; + int ret; +#endif + int interrupts[INTR_RANGE_COUNT]; + + if (g_softc) { + device_printf(dev, + "Error, only one MSI-X controller is supported\n"); + return (ENXIO); + } + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + sc->sc_dev = dev; + + rid = 0; + sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->reg == NULL) { + device_printf(dev, "Failed to allocate resource\n"); + return (ENXIO); + } + + OF_getencprop(OF_parent(node), "#address-cells", + &par_addr, sizeof(par_addr)); + + OF_getencprop(OF_parent(node), "#size-cells", + &par_size, sizeof(par_size)); + + OF_getencprop(node, "reg", (pcell_t*)reg, sizeof(reg)); + + if (par_addr == 1) + reg_base = (uint64_t)reg[0]; + else + reg_base = OFW_CELL_TO_UINT64(®[0]); + + if (par_size == 1) + reg_size = (uint64_t)reg[par_addr]; + else + reg_size = OFW_CELL_TO_UINT64(®[par_addr]); + + sc->base_addr = (bus_addr_t)reg_base; + + memset(interrupts, 0, sizeof(interrupts)); + +#ifdef ARM_INTRNG + xref = OF_xref_from_node(node); + + /* Register this device as interrupt controller */ + if (intr_pic_register(dev, xref)) + device_printf(dev, "could not register MSI-X controller\n"); + else + device_printf(dev, "MSI-X controller registered\n"); + + OF_device_register_xref(xref, dev); + + /* Find root interrupt controller */ + iparent = ofw_bus_find_iparent(node); + if (iparent == 0) { + device_printf(dev, "No interrupt-parrent found. " + "Error in DTB\n"); + return (ENXIO); + } else { + /* While at parent - store interrupt cells prop */ + if (OF_searchencprop(OF_node_from_xref(iparent), + "#interrupt-cells", &icells, sizeof(icells)) == -1) { + device_printf(dev, "DTB: Missing #interrupt-cells " + "property in GIC node\n"); + return (ENXIO); + } + } + + gic_dev = OF_device_from_xref(iparent); + if (gic_dev == NULL) { + device_printf(dev, "Cannot find GIC device\n"); + return (ENXIO); + } + sc->gic_dev = gic_dev; + + /* Manually read range of interrupts from DTB */ + nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), + (void **)&intr); + if (nintr == 0) { + device_printf(dev, "Cannot read interrupts prop from DTB\n"); + return (ENXIO); + } else if ((nintr / icells) != INTR_RANGE_COUNT) { + /* Supposed to have min and max value only */ + device_printf(dev, "Unexpected count of interrupts " + "in DTB node\n"); + return (EINVAL); + } + + /* Read interrupt range values */ + for (i = 0; i < INTR_RANGE_COUNT; i++ ) + interrupts[i] = intr[(i * icells) + IRQ_OFFSET]; + +#else /* !ARM_INTRNG */ + memset(&rl, 0, sizeof(rl)); + resource_list_init(&rl); + ret = ofw_bus_intr_to_rl(dev, node, &rl, NULL); + if (ret) { + device_printf(dev, "Failed to get interrupt data\n"); + return (ENXIO); + } + + ret = 0; + STAILQ_FOREACH(rle, &rl, link) { + if (interrupts_count >= + ((sizeof(interrupts) / sizeof(interrupts[0])))) { + ret = ENOMEM; + break; + } + interrupts[interrupts_count] = rle->start; + interrupts_count++; + } + resource_list_free(&rl); + if (ret || (interrupts_count != 2)) { + device_printf(dev, "Unable to parse MSI-X IRQ assignment\n"); + return (ENXIO); + } +#endif /* ARM_INTRNG */ + sc->irq_min = interrupts[0]; + sc->irq_max = interrupts[1]; + sc->irq_count = (sc->irq_max - sc->irq_min + 1); + + if (sc->irq_count > MAX_MSIX_COUNT) + sc->irq_max = sc->irq_min + MAX_MSIX_COUNT - 1; + +#ifdef ARM_INTRNG + /* Create FDT mapping for whole MSI-X interrupt range */ + for (i = 0; i < sc->irq_count; i++) { + /* Synthetic map addition */ + cells[IRQ_OFFSET] = sc->irq_min + i; + vec = intr_fdt_map_irq(node, cells, + (sizeof(cells) / sizeof(cells[0]))); + if (vec == NIRQ) { + device_printf(dev, + "Unable to map IRQ s%d\n", + cells[IRQ_OFFSET]); + return (ENXIO); + } + + /* Store both irq numbers (HW and system) */ + sc->intr_map[i].irq = cells[IRQ_OFFSET]; + sc->intr_map[i].uintr = vec; + } +#endif + mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF); + + device_printf(dev, + "MSI-X base address 0x%jx, size 0x%jx, IRQ %d-%d\n", + (uintmax_t)reg_base, (uintmax_t)reg_size, sc->irq_min, sc->irq_max); + + g_softc = sc; + + return (bus_generic_attach(dev)); +} + +#ifdef ARM_INTRNG +static int +al_find_uintr_pos_in_map(int uintr) +{ + int i; + + for (i = 0; i < MAX_MSIX_COUNT; i++) + if (g_softc->intr_map[i].uintr == uintr) + return (i); + return (ERR_NOT_IN_MAP); +} +#endif + +int +al_msix_map_msi(int intr, uint64_t *addr, uint32_t *data) +{ + int irq = 0; +#ifdef ARM_INTRNG + int i; + + /* Find system interrupt in interrupt map */ + if ((i = al_find_uintr_pos_in_map(intr)) == ERR_NOT_IN_MAP) { + device_printf(g_softc->sc_dev, "IRQ%d not mapped in GIC\n", intr); + return (EINVAL); + } + + /* Validate parameters */ + if (isclr(&g_softc->msi_bitmap, i)) { + device_printf(g_softc->sc_dev, "IRQ%d mapped but not allocated\n", + intr); + return (EINVAL); + } + + /* Get hardware interrupt number for address calculation */ + irq = g_softc->intr_map[i].irq; +#else + /* Check if IRQ number is in range */ + if ((intr > g_softc->irq_max) || (intr < g_softc->irq_min)) + return (EINVAL); + + /* validate parameters */ + if (isclr(&g_softc->msi_bitmap, intr - g_softc->irq_min)) { + device_printf(g_softc->sc_dev, "invalid IRQ: %d\n", intr); + return (EINVAL); + } + + /* Remove SPI offset from interrupt number */ + irq = intr - SPI_OFFSET; +#endif + /* + * MSIX message address format: + * [63:20] - MSIx TBAR + * Same value as the MSIx Translation Base Address Register + * [19] - WFE_EXIT + * Once set by MSIx message, an EVENTI is signal to the CPUs + * cluster specified by ‘Local GIC Target List’ + * [18:17] - Target GIC ID + * Specifies which IO-GIC (external shared GIC) is targeted + * 0: Local GIC, as specified by the Local GIC Target List + * 1: IO-GIC 0 + * 2: Reserved + * 3: Reserved + * [16:13] - Local GIC Target List + * Specifies the Local GICs list targeted by this MSIx + * message. + * [16] If set, SPIn is set in Cluster 0 local GIC + * [15:13] Reserved + * [15] If set, SPIn is set in Cluster 1 local GIC + * [14] If set, SPIn is set in Cluster 2 local GIC + * [13] If set, SPIn is set in Cluster 3 local GIC + * [12:3] - SPIn + * Specifies the SPI (Shared Peripheral Interrupt) index to + * be set in target GICs + * Notes: + * If targeting any local GIC than only SPI[249:0] are valid + * [2] - Function vector + * MSI Data vector extension hint + * [1:0] - Reserved + * Must be set to zero + */ + *addr = (uint64_t)g_softc->base_addr + (uint64_t)((1<<16) + + (irq << 3)); + *data = 0; +#ifndef ARM_INTRNG + /* Controller expects to get positive-edge-triggered interrupt */ + if (bus_generic_config_intr(g_softc->sc_dev, intr, + INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH)) { + device_printf(g_softc->sc_dev, + "unable to configure interrupt\n"); + return (EINVAL); + } +#endif + if (bootverbose) + device_printf(g_softc->sc_dev, + "MSI mapping: irq: s%d addr: %jx data: %x\n", + irq, *addr, *data); + return (0); +} + +int +al_msix_alloc_msi(int count, int *irqs) +{ + u_int start = 0; + u_int i; + + if ((powerof2(count) == 0) || (count > 8)) + return (EINVAL); + + mtx_lock(&g_softc->msi_mtx); + + for (start = 0; (start + count) < (g_softc->irq_count); start++) { + for (i = start; i < start + count; i++) { + if (isset(&g_softc->msi_bitmap, i)) + break; + } + if (i == (start + count)) + break; + } + + if ((start + count) >= g_softc->irq_max) { + mtx_unlock(&g_softc->msi_mtx); + return (ENXIO); + } + + for (i = start; i < start + count; i++) { + setbit(&g_softc->msi_bitmap, i); +#ifdef ARM_INTRNG + *irqs++ = g_softc->intr_map[i].uintr; +#else + *irqs++ = g_softc->irq_min + i; +#endif + } + + if (bootverbose) + device_printf(g_softc->sc_dev, + "MSI-X allocation: start IRQ %d, count %d\n", + start + g_softc->irq_min, count); + + mtx_unlock(&g_softc->msi_mtx); + + return (0); +} + +int +al_msix_release_msi(int count, int *irqs) +{ + int i; + + mtx_lock(&g_softc->msi_mtx); + + for (i = 0; i < count; i++) +#ifdef ARM_INTRNG + if (al_find_uintr_pos_in_map(irqs[i]) != ERR_NOT_IN_MAP) + clrbit(&g_softc->msi_bitmap, i); +#else + clrbit(&g_softc->msi_bitmap, irqs[i] - g_softc->irq_min); +#endif + + mtx_unlock(&g_softc->msi_mtx); + + return (0); +} + +#ifdef ARM_INTRNG +static int +al_msix_ic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) +{ + device_t gic_dev = g_softc->gic_dev; + + return (PIC_REGISTER(gic_dev, isrc, is_percpu)); +} + +static void +al_msix_ic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + device_t gic_dev = g_softc->gic_dev; + + PIC_ENABLE_INTR(gic_dev, isrc); +} + +static void +al_msix_ic_enable_source(device_t dev, struct intr_irqsrc *isrc) +{ + device_t gic_dev = g_softc->gic_dev; + + PIC_ENABLE_SOURCE(gic_dev, isrc); +} + +static int +al_msix_ic_unregister(device_t dev, struct intr_irqsrc *isrc) +{ + device_t gic_dev = g_softc->gic_dev; + + return (PIC_UNREGISTER(gic_dev, isrc)); +} + +static void +al_msix_ic_disable_source(device_t dev, struct intr_irqsrc *isrc) +{ + device_t gic_dev = g_softc->gic_dev; + + PIC_DISABLE_SOURCE(gic_dev, isrc); +} +#endif Index: sys/arm/annapurna/alpine/files.alpine =================================================================== --- sys/arm/annapurna/alpine/files.alpine +++ sys/arm/annapurna/alpine/files.alpine @@ -6,6 +6,9 @@ arm/versatile/versatile_timer.c standard dev/uart/uart_dev_ns8250.c optional uart +arm/annapurna/alpine/alpine_pci.c optional pci +arm/annapurna/alpine/alpine_pci_msix.c optional pci +contrib/alpine-hal/al_hal_pcie.c optional pci arm/annapurna/alpine/common.c standard arm/annapurna/alpine/alpine_machdep.c standard arm/annapurna/alpine/alpine_machdep_mp.c optional smp Index: sys/arm/conf/ALPINE =================================================================== --- sys/arm/conf/ALPINE +++ sys/arm/conf/ALPINE @@ -63,8 +63,15 @@ # Serial ports device uart +#PCI/PCIE +device pci +options ALPINE_PCI_MSI +options ALPINE_PCI_MSIX + # Ethernet device ether +device re +device em device mii device bpf options DEVICE_POLLING Index: sys/boot/fdt/dts/arm/annapurna-alpine.dts =================================================================== --- sys/boot/fdt/dts/arm/annapurna-alpine.dts +++ sys/boot/fdt/dts/arm/annapurna-alpine.dts @@ -164,11 +164,22 @@ }; }; + /* MSIX Configuration */ + msix { + compatible = "annapurna-labs,al-msix"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0xfbe00000 0x100000>; + interrupts = <0 96 1 0 159 1>; + interrupt-parent = <&MPIC>; + }; + pcie-internal { compatible = "annapurna-labs,al-internal-pcie"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; + reg = <0xfe000000 0x1000000>; interrupt-parent = <&MPIC>; interrupt-map-mask = <0xf800 0 0 7>; interrupt-map = <0x3000 0 0 1 &MPIC 0 32 4>, // USB adapter Index: sys/conf/options.arm =================================================================== --- sys/conf/options.arm +++ sys/conf/options.arm @@ -1,4 +1,6 @@ #$FreeBSD$ +ALPINE_PCI_MSI opt_alpine.h +ALPINE_PCI_MSIX opt_alpine.h ARMV6 opt_global.h ARM_CACHE_LOCK_ENABLE opt_global.h ARM_INTRNG opt_global.h