Page MenuHomeFreeBSD

D2579.id10643.diff
No OneTemporary

D2579.id10643.diff

Index: sys/arm/annapurna/alpine/alpine_pci.h
===================================================================
--- /dev/null
+++ sys/arm/annapurna/alpine/alpine_pci.h
@@ -0,0 +1,51 @@
+/*-
+********************************************************************************
+Copyright (C) 2015 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,1617 @@
+/*-
+ * Copyright (c) 2008 MARVELL INTERNATIONAL LTD.
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * Copyright (c) 2010-2015 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/intr.h>
+#include <machine/fdt.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#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 <machine/resource.h>
+#include <machine/bus.h>
+#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_RANGES_CELLS ((3 + 3 + 2) * 3)
+#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)
+
+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,al-external-pcie", AL_PCI_TYPE_EXTERNAL},
+ {NULL, 0}
+};
+
+struct al_pci_range {
+ u_long base_pci;
+ u_long base_parent;
+ u_long len;
+};
+
+typedef struct {
+ 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)];
+ struct ofw_bus_iinfo sc_pci_iinfo;
+} 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 *, u_long, u_long, u_long, u_int);
+static int al_pcib_release_resource(device_t, device_t, int, int,
+ struct resource *);
+static int al_pcib_read_ivar(device_t, device_t, int, uintptr_t *);
+static int al_pcib_write_ivar(device_t, device_t, int, uintptr_t);
+static int al_pcib_init(al_pcib_softc *, int, int);
+static void al_pcib_enable(al_pcib_softc *);
+static int al_pcib_write_ivar(device_t, device_t, int, uintptr_t);
+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 int al_pcib_route_interrupt(device_t, device_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 *);
+static int al_pcie_getproperty(phandle_t, pcell_t *, pcell_t *, pcell_t *,
+ pcell_t *);
+static int al_pcie_nranges(phandle_t, pcell_t *, pcell_t *, pcell_t *);
+
+#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, al_pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, al_pcib_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, al_pcib_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),
+};
+
+devclass_t pcib_devclass;
+
+DRIVER_MODULE(pcib, ofwbus, al_pcib_driver, 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);
+
+ /* 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_pci_iinfo, sizeof(pcell_t));
+
+ if (bootverbose) {
+ device_printf(dev,
+ "bridge space: base_parent = 0x%lx, base_pci = 0x%lx "
+ "len = %lx\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%lx, base_pci = 0x%lx "
+ "len = %lx\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%lx, base_pci = 0x%lx "
+ "len = %lx\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 (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[FDT_RANGES_CELLS];
+ struct al_pci_range *pci_space;
+ pcell_t addr_cells, size_cells, par_addr_cells;
+ pcell_t *rangesptr;
+ pcell_t cell0, cell1, cell2;
+ int tuples, i, rv, offset_cells;
+
+ if (al_pcie_getproperty(node, &par_addr_cells, &addr_cells,
+ &size_cells, ranges) != 0) {
+ device_printf(sc->sc_dev,
+ "Unexpected number of address or size cells in FDT "
+ " %d:%d:%d\n",
+ par_addr_cells, addr_cells, size_cells);
+ return (ENXIO);
+ }
+
+ tuples = al_pcie_nranges(node, &par_addr_cells, &addr_cells,
+ &size_cells);
+ if (tuples < 0)
+ return (ERANGE);
+
+ /*
+ * 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 = bswap32(*((uint32_t *)rangesptr));
+ rangesptr++;
+ cell1 = bswap32(*((uint32_t *)rangesptr));
+ rangesptr++;
+ cell2 = bswap32(*((uint32_t *)rangesptr));
+ 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 out;
+ }
+
+ if (par_addr_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 ((par_addr_cells - offset_cells) == 1)
+ pci_space->base_parent =
+ bswap32(*((uint32_t *)rangesptr));
+ else
+ pci_space->base_parent =
+ bswap64(*((uint64_t *)rangesptr));
+
+ rangesptr += par_addr_cells - offset_cells;
+
+ if (size_cells == 1)
+ pci_space->len =
+ bswap32(*((uint32_t *)rangesptr));
+ else
+ pci_space->len =
+ bswap64(*((uint64_t *)rangesptr));
+
+ rangesptr += size_cells;
+ pci_space->base_pci = cell2;
+ }
+
+ rv = 0;
+out:
+ return (rv);
+}
+
+static int
+al_pcie_getproperty(phandle_t node, pcell_t *par_addr, pcell_t *pci_addr,
+ pcell_t *size, pcell_t *ranges)
+{
+
+ if (OF_getencprop(node, "#address-cells", pci_addr,
+ sizeof(*pci_addr)))
+ *pci_addr = 3;
+
+ if (OF_getencprop(node, "#size-cells", size,
+ sizeof(*size)))
+ *size = 2;
+
+ if (OF_getencprop(OF_parent(node), "#address-cells",
+ par_addr, sizeof(*par_addr)))
+ *par_addr = 1;
+
+ OF_getprop(node, "ranges", (void *)ranges,
+ FDT_RANGES_CELLS*sizeof(*ranges));
+
+ if ((*par_addr != 1) || (*pci_addr != 3) ||
+ (*size != 2))
+ return (ERANGE);
+
+ return (0);
+}
+
+static int
+al_pcie_nranges(phandle_t node, pcell_t *par_addr, pcell_t *pci_addr,
+ pcell_t *size)
+{
+ int len;
+
+ len = OF_getproplen(node, "ranges");
+ if (len <= 0)
+ return (-1);
+
+ return ((len / sizeof(cell_t)) /
+ (*pci_addr + *par_addr + *size));
+}
+
+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 %llx, limit %llx, target %llx\n",
+ __func__, io_atu_region.base_addr,
+ io_atu_region.limit, 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, &reg) != 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, &reg) != 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, &reg) != 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, &reg) != 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, &reg) != 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, &reg) != 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,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+ struct rman *rm = NULL;
+ struct resource *res;
+ u_long bus_start = 0, bus_size = 0;
+
+ if (bootverbose)
+ device_printf(dev, "al_pcib_alloc_resource begin, start=0x%lx, "
+ "end=0x%lx, count=0x%lx, type=%d\n",
+ start, end, 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_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_busnr;
+ return (0);
+ case PCIB_IVAR_DOMAIN:
+ *result = device_get_unit(dev);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static int
+al_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->sc_busnr = value;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+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);
+
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "al_pcib_read_config begin addr=%p bus=%d, slot=%d, "
+ "func=%d reg=%d bytes=%d val=%x\n",
+ (void*)addr, bus, slot, func, reg, bytes, val);
+
+ 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);
+
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "al_pcib_write_config begin addr=%p bus=%d, slot=%d, "
+ "func=%d reg=%d bytes=%d val=%x\n",
+ (void*)addr, bus, slot, func, reg, bytes, val);
+
+ return;
+}
+
+static int
+al_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+ al_pcib_softc *sc;
+ struct ofw_pci_register reg;
+ uint32_t pintr, mintr[4];
+ int icells;
+ phandle_t iparent;
+
+ sc = device_get_softc(pcib);
+ pintr = pin;
+
+ /* Fabricate imap information in case this isn't an OFW device */
+ bzero(&reg, sizeof(reg));
+ reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+ (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+ (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+ icells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo,
+ &reg, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
+ &iparent);
+ if (icells > 0)
+ return (ofw_bus_map_intr(dev, iparent, icells, mintr));
+
+ /* Maybe it's a real interrupt, not an intpin */
+ if (pin > 4)
+ return (pin);
+
+ device_printf(pcib, "could not route pin %d for device %d.%d\n",
+ pin, pci_get_slot(dev), pci_get_function(dev));
+ return (PCI_INVALID_IRQ);
+}
+
+#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,336 @@
+/*-
+*******************************************************************************
+Copyright (C) 2015 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/intr.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "alpine_pci.h"
+#include "opt_alpine.h"
+
+#define MAX_MSIX_COUNT 128
+
+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 *);
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t al_msix_methods[] = {
+ DEVMETHOD(device_probe, al_msix_probe),
+ DEVMETHOD(device_attach, al_msix_attach),
+
+ 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];
+};
+
+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);
+
+static int
+al_msix_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "annapurna-labs,al-msix")) {
+ device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+al_msix_attach(device_t dev)
+{
+ struct al_msix_softc *sc;
+ struct resource_list rl;
+ struct resource_list_entry *rle;
+ phandle_t node;
+ uint64_t reg_base = 0;
+ uint64_t reg_size = 0;
+ int ret, rid;
+ int interrupts[2];
+ int interrupts_count = 0;
+ pcell_t reg[2];
+ pcell_t par_addr, par_size;
+
+ 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_getprop(node, "reg", (void *)reg, sizeof(reg));
+
+ if (par_addr == 1)
+ reg_base = bswap32((uint32_t)reg[0]);
+ else
+ reg_base = bswap64((uint64_t)reg[0]);
+
+ if (par_size == 1)
+ reg_size = bswap32((uint32_t)reg[1]);
+ else
+ reg_size = bswap64((uint64_t)reg[1]);
+
+ sc->base_addr = (bus_addr_t)reg_base;
+
+ 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);
+ }
+
+ memset(interrupts, 0, sizeof(interrupts));
+ 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);
+ }
+
+ 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;
+
+ mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
+
+ device_printf(dev,
+ "MSI-X base address 0x%llx, size 0x%llx, IRQ %d-%d\n",
+ reg_base, reg_size, sc->irq_min, sc->irq_max);
+
+ g_softc = sc;
+
+ return (bus_generic_attach(dev));
+}
+
+int
+al_msix_map_msi(int irq, uint64_t *addr, uint32_t *data)
+{
+ int ret;
+
+ /* Check if IRQ number is in range */
+ if ((irq > g_softc->irq_max) || (irq < g_softc->irq_min))
+ return (EINVAL);
+
+ /* validate parameters */
+ if (isclr(&g_softc->msi_bitmap, irq - g_softc->irq_min)) {
+ device_printf(g_softc->sc_dev, "invalid MSI 0x%x\n", irq);
+ return (EINVAL);
+ }
+
+ /*
+ * 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
+ *
+ * The SPI offset is 32, so decrement IRQ number.
+ */
+ *addr = (uint64_t)g_softc->base_addr + (uint64_t)((1<<16) +
+ ((irq - 32) << 3));
+ *data = 0;
+
+ /* Controller expects to get positive-edge-triggered interrupt */
+ if ((arm_config_irq == NULL) || (ret = arm_config_irq(irq,
+ INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH))) {
+
+ device_printf(g_softc->sc_dev,
+ "unable to configure interrupt, aborting\n");
+ return (EINVAL);
+ }
+
+ device_printf(g_softc->sc_dev,
+ "MSI mapping: irq: %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);
+ *irqs++ = g_softc->irq_min + i;
+ }
+
+ 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++)
+ clrbit(&g_softc->msi_bitmap, irqs[i] - g_softc->irq_min);
+
+ mtx_unlock(&g_softc->msi_mtx);
+
+ return (0);
+}
Index: sys/arm/annapurna/alpine/files.alpine
===================================================================
--- sys/arm/annapurna/alpine/files.alpine
+++ sys/arm/annapurna/alpine/files.alpine
@@ -7,6 +7,9 @@
dev/uart/uart_dev_ns8250.c optional uart
dev/ofw/ofw_cpu.c standard
+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
@@ -62,8 +62,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

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 21, 12:30 PM (7 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27112903
Default Alt Text
D2579.id10643.diff (56 KB)

Event Timeline