Page MenuHomeFreeBSD

D2579.1775615461.diff
No OneTemporary

Size
59 KB
Referenced Files
None
Subscribers
None

D2579.1775615461.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,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 <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 <dev/ofw/ofwpci.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_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, &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,
+ 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 <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"
+
+#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(&reg[0]);
+
+ if (par_size == 1)
+ reg_size = (uint64_t)reg[par_addr];
+ else
+ reg_size = OFW_CELL_TO_UINT64(&reg[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

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 8, 2:31 AM (1 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28297358
Default Alt Text
D2579.1775615461.diff (59 KB)

Event Timeline