Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144591144
D2579.1775615461.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
59 KB
Referenced Files
None
Subscribers
None
D2579.1775615461.diff
View Options
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, ®) != 0) {
+ device_printf(sc->sc_dev, "Failed to find PCIY_EXPRESS cap\n");
+ return (0);
+ }
+
+ ctl = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+ reg + PCIER_DEVICE_CTL, 2);
+
+ return (PCIE_MIN_MPS << ((ctl & PCIEM_CTL_MAX_PAYLOAD) >>
+ PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS));
+}
+
+/**
+ * get_pcie_max_payload - get PCI Express maximum supported payload size
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ * or appropriate error value.
+ */
+static uint16_t
+get_pcie_max_payload(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ uint16_t reg16;
+ int reg;
+
+ if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0)
+ return (0);
+
+ reg16 = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, reg + PCIER_DEVICE_CAP, 2);
+ return (reg16 & PCIEM_CAP_MAX_PAYLOAD);
+}
+
+/**
+ * get_pcie_get_type - get PCI Express device type (device or bridge)
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in byte, rids
+ * or appropriate error value.
+ */
+static uint16_t
+get_pcie_get_type(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ uint16_t reg16;
+ int reg;
+
+ if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0)
+ return (0);
+
+ reg16 = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, reg + PCIER_FLAGS, 2);
+ return (reg16 & PCIEM_FLAGS_TYPE);
+}
+
+/**
+ * pcie_set_mps - set PCI Express maximum payload size
+ * @dev: PCI device to query
+ * @mps: maximum payload size in bytes
+ * valid values are 128, 256, 512, 1024, 2048, 4096
+ *
+ * If possible sets maximum payload size
+ */
+static int
+pcie_set_mps(al_pcib_softc *sc, int mps, int bus, int slot, int func)
+{
+ uint16_t v, tmp;
+ int reg;
+
+ if (mps < PCIE_MIN_MPS || mps > PCIE_MAX_MPS ||
+ powerof2(mps) == 0) {
+ return (EFAULT);
+ }
+
+ v = ffs(mps) - 8;
+ if (v > get_pcie_max_payload(sc, bus, slot, func))
+ return (EFAULT);
+ v <<= PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS;
+
+ if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0)
+ return (EFAULT);
+
+ tmp = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+ reg + PCIER_DEVICE_CTL, 2);
+ tmp &= ~PCIEM_CTL_MAX_PAYLOAD;
+ tmp |= (v & PCIEM_CTL_MAX_PAYLOAD);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func,
+ reg + PCIER_DEVICE_CTL, tmp, 2);
+
+ return (0);
+}
+
+static void
+pcie_write_mps(al_pcib_softc *sc, int mps, int bus, int slot, int func)
+{
+ int rc;
+ device_t parent;
+ uint16_t type;
+
+ mps = PCIE_MIN_MPS << get_pcie_max_payload(sc, bus, slot, func);
+
+ parent = device_get_parent(sc->sc_dev);
+ type = get_pcie_get_type(sc, bus, slot, func);
+
+ mps = min(mps, AL_MAX_SUPPORTED_MPS);
+
+ rc = pcie_set_mps(sc, mps, bus, slot, func);
+ if (rc != 0) {
+ device_printf(sc->sc_dev,
+ "Failed attempting to set the MPS %d\n", mps);
+ }
+}
+
+static uint16_t
+pcie_get_readrq(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ uint16_t ctl;
+ int reg;
+
+ if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0)
+ return (0);
+
+ ctl = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+ reg + PCIER_DEVICE_CTL, 2);
+
+ return (PCIE_MIN_MRRS << ((ctl & PCIEM_CTL_MAX_READ_REQUEST) >>
+ PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS));
+}
+
+/**
+ * pcie_set_readrq - set PCI Express maximum memory read request
+ * @dev: PCI device to query
+ * @rq: maximum memory read count in bytes
+ * valid values are 128, 256, 512, 1024, 2048, 4096
+ *
+ * If possible sets maximum memory read request in bytes
+ */
+static int
+pcie_set_readrq(al_pcib_softc *sc, int rq, int bus, int slot, int func)
+{
+ uint16_t v, tmp;
+ int mps, reg;
+
+ if (rq < PCIE_MIN_MRRS || rq > PCIE_MAX_MRRS || powerof2(rq) == 0) {
+ return (EINVAL);
+ }
+
+ mps = pcie_get_mps(sc, bus, slot, func);
+
+ if (mps < 0)
+ return (mps);
+ if (mps < rq)
+ rq = mps;
+
+ v = (ffs(rq) - 8) << PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS;
+
+ if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 0)
+ return (EFAULT);
+
+ tmp = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, reg + PCIER_DEVICE_CTL, 2);
+ tmp &= ~PCIEM_CTL_MAX_READ_REQUEST;
+ tmp |= (v & PCIEM_CTL_MAX_READ_REQUEST);
+ al_pcib_write_config(sc->sc_dev, bus, slot,
+ func, reg + PCIER_DEVICE_CTL, tmp, 2);
+
+ return (0);
+}
+
+static void
+pcie_write_mrrs(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ int rc, mrrs;
+
+ /* For Max performance, the MRRS must be set to the largest supported
+ * value. However, it cannot be configured larger than the MPS the
+ * device or the bus can support. This should already be properly
+ * configured by a prior call to pcie_write_mps.
+ */
+ mrrs = pcie_get_mps(sc, bus, slot, func);
+
+ /* MRRS is a R/W register. Invalid values can be written, but a
+ * subsequent read will verify if the value is acceptable or not.
+ * If the MRRS value provided is not acceptable (e.g., too large),
+ * shrink the value until it is acceptable to the HW.
+ */
+ while (mrrs != pcie_get_readrq(sc, bus, slot, func) && mrrs >=
+ PCIE_MIN_MRRS) {
+ rc = pcie_set_readrq(sc, mrrs, bus, slot, func);
+ if (rc == 0)
+ break;
+
+ device_printf(sc->sc_dev,
+ "Failed attempting to set the MRRS\n");
+ mrrs /= 2;
+ }
+
+ if (mrrs < PCIE_MIN_MRRS)
+ device_printf(sc->sc_dev,
+ "MRRS was unable to be configured with a "
+ "safe value. If problems are experienced, try running "
+ "with pci=pcie_bus_safe.\n");
+}
+
+static int
+pcie_bus_configure_set(al_pcib_softc *sc, int bus, int slot, int func,
+ uint8_t *data)
+{
+ int mps, orig_mps;
+
+ mps = PCIE_MIN_MPS << *(uint8_t *)data;
+
+ orig_mps = pcie_get_mps(sc, bus, slot, func);
+
+ pcie_write_mps(sc, mps, bus, slot, func);
+ pcie_write_mrrs(sc, bus, slot, func);
+
+ device_printf(sc->sc_dev,
+ "PCI-E Max Payload Size set to %4d/%4d (was %4d), "
+ "Max Read Rq %4d\n", pcie_get_mps(sc, bus, slot, func),
+ PCIE_MIN_MPS << get_pcie_max_payload(sc, bus, slot, func),
+ orig_mps, pcie_get_readrq(sc, bus, slot, func));
+
+ return (0);
+}
+
+static void
+al_pcib_fixup(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ uint32_t val;
+ uint8_t smpss = 0;
+ pcie_bus_configure_set(sc, bus, slot, func, &smpss);
+
+ /* Do not run fixups on external PCIe bus */
+ if (bus != 0)
+ return;
+
+ val = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+ AL_PCI_AXI_CFG_AND_CTR_0, 4);
+ val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_2_PF_VEC_PH_VEC_OVRD_FROM_AXUSER_MASK;
+ al_pcib_write_config(sc->sc_dev, bus, slot, func,
+ AL_PCI_AXI_CFG_AND_CTR_0, val, 4);
+
+ val = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+ AL_PCI_APP_CONTROL, 4);
+ val &= ~0xffff;
+ val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_4_PF_VEC_MEM_ADDR54_63_SEL_VMID_MASK;
+ al_pcib_write_config(sc->sc_dev, bus, slot, func,
+ AL_PCI_APP_CONTROL, val, 4);
+}
+
+static int
+al_pcib_init(al_pcib_softc *sc, int bus, int maxslot)
+{
+ int slot, func, maxfunc, error, buscnt;
+ uint8_t hdrtype, command, class, subclass;
+
+ buscnt = sc->sc_busnr;
+
+ for (slot = 0; slot <= maxslot; slot++) {
+ maxfunc = 0;
+ for (func = 0; func <= maxfunc; func++) {
+ hdrtype = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_HDRTYPE, 1);
+
+ if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+ continue;
+
+ if (func == 0 && (hdrtype & PCIM_MFDEV) != 0)
+ maxfunc = PCI_FUNCMAX;
+
+ command = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_COMMAND, 1);
+ command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func,
+ PCIR_COMMAND, command, 1);
+
+ error = al_pcib_init_all_bars(sc, bus, slot, func,
+ hdrtype);
+
+ if (error)
+ return (error);
+
+ command |= PCIM_CMD_PORTEN;
+ al_pcib_write_config(sc->sc_dev, bus, slot, func,
+ PCIR_COMMAND, command, 1);
+
+ /* Handle PCI-PCI bridges */
+ class = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_CLASS, 1);
+ subclass = al_pcib_read_config(sc->sc_dev, bus, slot,
+ func, PCIR_SUBCLASS, 1);
+
+ al_pcib_fixup(sc, bus, slot, func);
+
+ if (class != PCIC_BRIDGE ||
+ subclass != PCIS_BRIDGE_PCI) {
+ continue;
+ }
+
+ /* Write appropriate bus number */
+ buscnt++;
+ al_pcib_write_config(sc->sc_dev, bus,0,0,
+ PCIR_SECBUS_1, buscnt, 1);
+ al_pcib_write_config(sc->sc_dev, bus,0,0,
+ PCIR_SUBBUS_1, buscnt, 1);
+
+ al_pcib_init_bridge(sc, bus, slot, func);
+ }
+ }
+
+ return (0);
+}
+
+static void
+al_pcib_enable(al_pcib_softc *sc)
+{
+ uint32_t val;
+
+ /*
+ * Enable PCI bridge.
+ */
+ val = al_pcib_read_config(sc->sc_dev, sc->sc_busnr, 0, 0,
+ PCIR_COMMAND, 4);
+ val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN |
+ PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
+ al_pcib_write_config(sc->sc_dev, sc->sc_busnr, 0, 0,
+ PCIR_COMMAND, val, 4);
+}
+
+static struct resource *
+al_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+ struct rman *rm = NULL;
+ struct resource *res;
+ rman_res_t bus_start = 0, bus_size = 0;
+
+ if (bootverbose)
+ device_printf(dev, "al_pcib_alloc_resource begin, start=0x%jx, "
+ "end=0x%jx, count=0x%jx, type=%d\n",
+ (uintmax_t)start, (uintmax_t)end, (uintmax_t)count, type);
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io_rman;
+ bus_start = sc->pci_range[AL_PCI_RANGE_IO].base_pci;
+ bus_size = sc->pci_range[AL_PCI_RANGE_IO].len;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem_rman;
+ bus_start = sc->pci_range[AL_PCI_RANGE_MEM].base_pci;
+ bus_size = sc->pci_range[AL_PCI_RANGE_MEM].len;
+ break;
+ default:
+ return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
+ type, rid, start, end, count, flags));
+ };
+
+ if ((start == 0UL) && (end == ~0UL)) {
+ start = bus_start;
+ end = bus_start + bus_size - 1;
+ count = bus_size;
+ }
+
+ if ((start < bus_start) || (start + count - 1 != end) ||
+ (end > bus_start + bus_size - 1)) {
+ device_printf(dev, "range failed\n");
+ return (NULL);
+ }
+
+ res = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (res == NULL) {
+ device_printf(dev, "rman_reserve_resource failed\n");
+ return (NULL);
+ }
+
+ rman_set_rid(res, *rid);
+
+ if ((flags & RF_ACTIVE) != 0)
+ if (bus_activate_resource(child, type, *rid, res)) {
+ rman_release_resource(res);
+ device_printf(dev, "bus_activate_resource failed\n");
+ return (NULL);
+ }
+
+ return (res);
+}
+
+static int
+al_pcib_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ case SYS_RES_MEMORY:
+ device_printf(dev, "Releasing SYS_RES_IOPORT and SYS_RES_MEMORY "
+ "resources is currently not supported. "
+ "This leads to memory leaks.\n");
+ break;
+ default:
+ return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
+ type, rid, res));
+ };
+
+ return (0);
+}
+
+static int
+al_pcib_maxslots(device_t dev)
+{
+
+ return (AL_PCI_MAX_SLOTS);
+}
+
+static uint32_t
+al_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ bus_addr_t addr;
+ uint32_t val = 0xffffffff;
+ al_pcib_softc *sc = device_get_softc(dev);
+ bus_addr_t handle = 0;
+ int ret;
+
+ mtx_lock(&sc->conf_lock);
+
+ ret = al_pcie_cfg_addr(dev, bus, slot, func,
+ reg & ~CONFIG_ADDR_ZERO_BITS_MASK, bytes, &handle, &addr);
+
+ if (ret != 0)
+ goto end;
+
+ val = bus_space_read_4(sc->reg_bst, handle, addr);
+
+ switch (bytes) {
+ case 1:
+ val = (val >>
+ ((reg & CONFIG_ADDR_BYTE_SELECTOR_MASK) * NBBY)) & 0xff;
+ break;
+ case 2:
+ val = (val >>
+ ((reg & CONFIG_ADDR_BYTE_SELECTOR_MASK) * NBBY)) & 0xffff;
+ break;
+ default:
+ break;
+ }
+
+end:
+ mtx_unlock(&sc->conf_lock);
+
+ return (val);
+}
+
+static void
+al_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ bus_addr_t addr;
+ al_pcib_softc *sc = device_get_softc(dev);
+ bus_addr_t handle = 0;
+ int ret;
+
+ mtx_lock(&sc->conf_lock);
+
+ ret = al_pcie_cfg_addr(dev, bus, slot, func, reg,
+ bytes, &handle, &addr);
+ if (ret != 0)
+ goto end;
+
+ switch (bytes) {
+ case 1:
+ bus_space_write_1(sc->reg_bst, handle, addr,
+ (uint8_t)val);
+ break;
+ case 2:
+ bus_space_write_2(sc->reg_bst, handle, addr,
+ (uint16_t)val);
+ break;
+ case 4:
+ bus_space_write_4(sc->reg_bst, handle, addr,
+ (uint32_t)val);
+ break;
+ default:
+ break;
+ }
+
+end:
+ mtx_unlock(&sc->conf_lock);
+
+ return;
+}
+
+#ifdef ALPINE_PCI_MSI
+
+static int
+al_pcib_map_msi(device_t dev __unused, device_t child __unused, int irq,
+ uint64_t *addr, uint32_t *data)
+{
+
+ return (al_msix_map_msi(irq, addr, data));
+}
+
+static int
+al_pcib_alloc_msi(device_t dev __unused, device_t child __unused, int count,
+ int maxcount __unused, int *irqs)
+{
+
+ return (al_msix_alloc_msi(count, irqs));
+}
+
+static int
+al_pcib_release_msi(device_t dev __unused, device_t child __unused,
+ int count, int *irqs)
+{
+
+ return (al_msix_release_msi(count, irqs));
+}
+#endif
+
+#ifdef ALPINE_PCI_MSIX
+static int
+al_pcib_alloc_msix(device_t dev __unused, device_t child __unused, int *irq)
+{
+
+ return (al_msix_alloc_msi(1, irq));
+}
+
+static int
+al_pcib_release_msix(device_t dev __unused, device_t child __unused, int irq)
+{
+
+ return (al_msix_release_msi(1, &irq));
+}
+#endif
Index: sys/arm/annapurna/alpine/alpine_pci_msix.c
===================================================================
--- /dev/null
+++ sys/arm/annapurna/alpine/alpine_pci_msix.c
@@ -0,0 +1,561 @@
+/*-
+*******************************************************************************
+Copyright (C) 2015,2016 Annapurna Labs Ltd.
+
+This file may be licensed under the terms of the Annapurna Labs Commercial
+License Agreement.
+
+Alternatively, this file can be distributed under the terms of the GNU General
+Public License V2 as published by the Free Software Foundation and can be
+found at http://www.gnu.org/licenses/gpl-2.0.html
+
+Alternatively, redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <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(®[0]);
+
+ if (par_size == 1)
+ reg_size = (uint64_t)reg[par_addr];
+ else
+ reg_size = OFW_CELL_TO_UINT64(®[par_addr]);
+
+ sc->base_addr = (bus_addr_t)reg_base;
+
+ memset(interrupts, 0, sizeof(interrupts));
+
+#ifdef ARM_INTRNG
+ xref = OF_xref_from_node(node);
+
+ /* Register this device as interrupt controller */
+ if (intr_pic_register(dev, xref))
+ device_printf(dev, "could not register MSI-X controller\n");
+ else
+ device_printf(dev, "MSI-X controller registered\n");
+
+ OF_device_register_xref(xref, dev);
+
+ /* Find root interrupt controller */
+ iparent = ofw_bus_find_iparent(node);
+ if (iparent == 0) {
+ device_printf(dev, "No interrupt-parrent found. "
+ "Error in DTB\n");
+ return (ENXIO);
+ } else {
+ /* While at parent - store interrupt cells prop */
+ if (OF_searchencprop(OF_node_from_xref(iparent),
+ "#interrupt-cells", &icells, sizeof(icells)) == -1) {
+ device_printf(dev, "DTB: Missing #interrupt-cells "
+ "property in GIC node\n");
+ return (ENXIO);
+ }
+ }
+
+ gic_dev = OF_device_from_xref(iparent);
+ if (gic_dev == NULL) {
+ device_printf(dev, "Cannot find GIC device\n");
+ return (ENXIO);
+ }
+ sc->gic_dev = gic_dev;
+
+ /* Manually read range of interrupts from DTB */
+ nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr),
+ (void **)&intr);
+ if (nintr == 0) {
+ device_printf(dev, "Cannot read interrupts prop from DTB\n");
+ return (ENXIO);
+ } else if ((nintr / icells) != INTR_RANGE_COUNT) {
+ /* Supposed to have min and max value only */
+ device_printf(dev, "Unexpected count of interrupts "
+ "in DTB node\n");
+ return (EINVAL);
+ }
+
+ /* Read interrupt range values */
+ for (i = 0; i < INTR_RANGE_COUNT; i++ )
+ interrupts[i] = intr[(i * icells) + IRQ_OFFSET];
+
+#else /* !ARM_INTRNG */
+ memset(&rl, 0, sizeof(rl));
+ resource_list_init(&rl);
+ ret = ofw_bus_intr_to_rl(dev, node, &rl, NULL);
+ if (ret) {
+ device_printf(dev, "Failed to get interrupt data\n");
+ return (ENXIO);
+ }
+
+ ret = 0;
+ STAILQ_FOREACH(rle, &rl, link) {
+ if (interrupts_count >=
+ ((sizeof(interrupts) / sizeof(interrupts[0])))) {
+ ret = ENOMEM;
+ break;
+ }
+ interrupts[interrupts_count] = rle->start;
+ interrupts_count++;
+ }
+ resource_list_free(&rl);
+ if (ret || (interrupts_count != 2)) {
+ device_printf(dev, "Unable to parse MSI-X IRQ assignment\n");
+ return (ENXIO);
+ }
+#endif /* ARM_INTRNG */
+ sc->irq_min = interrupts[0];
+ sc->irq_max = interrupts[1];
+ sc->irq_count = (sc->irq_max - sc->irq_min + 1);
+
+ if (sc->irq_count > MAX_MSIX_COUNT)
+ sc->irq_max = sc->irq_min + MAX_MSIX_COUNT - 1;
+
+#ifdef ARM_INTRNG
+ /* Create FDT mapping for whole MSI-X interrupt range */
+ for (i = 0; i < sc->irq_count; i++) {
+ /* Synthetic map addition */
+ cells[IRQ_OFFSET] = sc->irq_min + i;
+ vec = intr_fdt_map_irq(node, cells,
+ (sizeof(cells) / sizeof(cells[0])));
+ if (vec == NIRQ) {
+ device_printf(dev,
+ "Unable to map IRQ s%d\n",
+ cells[IRQ_OFFSET]);
+ return (ENXIO);
+ }
+
+ /* Store both irq numbers (HW and system) */
+ sc->intr_map[i].irq = cells[IRQ_OFFSET];
+ sc->intr_map[i].uintr = vec;
+ }
+#endif
+ mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
+
+ device_printf(dev,
+ "MSI-X base address 0x%jx, size 0x%jx, IRQ %d-%d\n",
+ (uintmax_t)reg_base, (uintmax_t)reg_size, sc->irq_min, sc->irq_max);
+
+ g_softc = sc;
+
+ return (bus_generic_attach(dev));
+}
+
+#ifdef ARM_INTRNG
+static int
+al_find_uintr_pos_in_map(int uintr)
+{
+ int i;
+
+ for (i = 0; i < MAX_MSIX_COUNT; i++)
+ if (g_softc->intr_map[i].uintr == uintr)
+ return (i);
+ return (ERR_NOT_IN_MAP);
+}
+#endif
+
+int
+al_msix_map_msi(int intr, uint64_t *addr, uint32_t *data)
+{
+ int irq = 0;
+#ifdef ARM_INTRNG
+ int i;
+
+ /* Find system interrupt in interrupt map */
+ if ((i = al_find_uintr_pos_in_map(intr)) == ERR_NOT_IN_MAP) {
+ device_printf(g_softc->sc_dev, "IRQ%d not mapped in GIC\n", intr);
+ return (EINVAL);
+ }
+
+ /* Validate parameters */
+ if (isclr(&g_softc->msi_bitmap, i)) {
+ device_printf(g_softc->sc_dev, "IRQ%d mapped but not allocated\n",
+ intr);
+ return (EINVAL);
+ }
+
+ /* Get hardware interrupt number for address calculation */
+ irq = g_softc->intr_map[i].irq;
+#else
+ /* Check if IRQ number is in range */
+ if ((intr > g_softc->irq_max) || (intr < g_softc->irq_min))
+ return (EINVAL);
+
+ /* validate parameters */
+ if (isclr(&g_softc->msi_bitmap, intr - g_softc->irq_min)) {
+ device_printf(g_softc->sc_dev, "invalid IRQ: %d\n", intr);
+ return (EINVAL);
+ }
+
+ /* Remove SPI offset from interrupt number */
+ irq = intr - SPI_OFFSET;
+#endif
+ /*
+ * MSIX message address format:
+ * [63:20] - MSIx TBAR
+ * Same value as the MSIx Translation Base Address Register
+ * [19] - WFE_EXIT
+ * Once set by MSIx message, an EVENTI is signal to the CPUs
+ * cluster specified by ‘Local GIC Target List’
+ * [18:17] - Target GIC ID
+ * Specifies which IO-GIC (external shared GIC) is targeted
+ * 0: Local GIC, as specified by the Local GIC Target List
+ * 1: IO-GIC 0
+ * 2: Reserved
+ * 3: Reserved
+ * [16:13] - Local GIC Target List
+ * Specifies the Local GICs list targeted by this MSIx
+ * message.
+ * [16] If set, SPIn is set in Cluster 0 local GIC
+ * [15:13] Reserved
+ * [15] If set, SPIn is set in Cluster 1 local GIC
+ * [14] If set, SPIn is set in Cluster 2 local GIC
+ * [13] If set, SPIn is set in Cluster 3 local GIC
+ * [12:3] - SPIn
+ * Specifies the SPI (Shared Peripheral Interrupt) index to
+ * be set in target GICs
+ * Notes:
+ * If targeting any local GIC than only SPI[249:0] are valid
+ * [2] - Function vector
+ * MSI Data vector extension hint
+ * [1:0] - Reserved
+ * Must be set to zero
+ */
+ *addr = (uint64_t)g_softc->base_addr + (uint64_t)((1<<16) +
+ (irq << 3));
+ *data = 0;
+#ifndef ARM_INTRNG
+ /* Controller expects to get positive-edge-triggered interrupt */
+ if (bus_generic_config_intr(g_softc->sc_dev, intr,
+ INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH)) {
+ device_printf(g_softc->sc_dev,
+ "unable to configure interrupt\n");
+ return (EINVAL);
+ }
+#endif
+ if (bootverbose)
+ device_printf(g_softc->sc_dev,
+ "MSI mapping: irq: s%d addr: %jx data: %x\n",
+ irq, *addr, *data);
+ return (0);
+}
+
+int
+al_msix_alloc_msi(int count, int *irqs)
+{
+ u_int start = 0;
+ u_int i;
+
+ if ((powerof2(count) == 0) || (count > 8))
+ return (EINVAL);
+
+ mtx_lock(&g_softc->msi_mtx);
+
+ for (start = 0; (start + count) < (g_softc->irq_count); start++) {
+ for (i = start; i < start + count; i++) {
+ if (isset(&g_softc->msi_bitmap, i))
+ break;
+ }
+ if (i == (start + count))
+ break;
+ }
+
+ if ((start + count) >= g_softc->irq_max) {
+ mtx_unlock(&g_softc->msi_mtx);
+ return (ENXIO);
+ }
+
+ for (i = start; i < start + count; i++) {
+ setbit(&g_softc->msi_bitmap, i);
+#ifdef ARM_INTRNG
+ *irqs++ = g_softc->intr_map[i].uintr;
+#else
+ *irqs++ = g_softc->irq_min + i;
+#endif
+ }
+
+ if (bootverbose)
+ device_printf(g_softc->sc_dev,
+ "MSI-X allocation: start IRQ %d, count %d\n",
+ start + g_softc->irq_min, count);
+
+ mtx_unlock(&g_softc->msi_mtx);
+
+ return (0);
+}
+
+int
+al_msix_release_msi(int count, int *irqs)
+{
+ int i;
+
+ mtx_lock(&g_softc->msi_mtx);
+
+ for (i = 0; i < count; i++)
+#ifdef ARM_INTRNG
+ if (al_find_uintr_pos_in_map(irqs[i]) != ERR_NOT_IN_MAP)
+ clrbit(&g_softc->msi_bitmap, i);
+#else
+ clrbit(&g_softc->msi_bitmap, irqs[i] - g_softc->irq_min);
+#endif
+
+ mtx_unlock(&g_softc->msi_mtx);
+
+ return (0);
+}
+
+#ifdef ARM_INTRNG
+static int
+al_msix_ic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
+{
+ device_t gic_dev = g_softc->gic_dev;
+
+ return (PIC_REGISTER(gic_dev, isrc, is_percpu));
+}
+
+static void
+al_msix_ic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ device_t gic_dev = g_softc->gic_dev;
+
+ PIC_ENABLE_INTR(gic_dev, isrc);
+}
+
+static void
+al_msix_ic_enable_source(device_t dev, struct intr_irqsrc *isrc)
+{
+ device_t gic_dev = g_softc->gic_dev;
+
+ PIC_ENABLE_SOURCE(gic_dev, isrc);
+}
+
+static int
+al_msix_ic_unregister(device_t dev, struct intr_irqsrc *isrc)
+{
+ device_t gic_dev = g_softc->gic_dev;
+
+ return (PIC_UNREGISTER(gic_dev, isrc));
+}
+
+static void
+al_msix_ic_disable_source(device_t dev, struct intr_irqsrc *isrc)
+{
+ device_t gic_dev = g_softc->gic_dev;
+
+ PIC_DISABLE_SOURCE(gic_dev, isrc);
+}
+#endif
Index: sys/arm/annapurna/alpine/files.alpine
===================================================================
--- sys/arm/annapurna/alpine/files.alpine
+++ sys/arm/annapurna/alpine/files.alpine
@@ -6,6 +6,9 @@
arm/versatile/versatile_timer.c standard
dev/uart/uart_dev_ns8250.c optional uart
+arm/annapurna/alpine/alpine_pci.c optional pci
+arm/annapurna/alpine/alpine_pci_msix.c optional pci
+contrib/alpine-hal/al_hal_pcie.c optional pci
arm/annapurna/alpine/common.c standard
arm/annapurna/alpine/alpine_machdep.c standard
arm/annapurna/alpine/alpine_machdep_mp.c optional smp
Index: sys/arm/conf/ALPINE
===================================================================
--- sys/arm/conf/ALPINE
+++ sys/arm/conf/ALPINE
@@ -63,8 +63,15 @@
# Serial ports
device uart
+#PCI/PCIE
+device pci
+options ALPINE_PCI_MSI
+options ALPINE_PCI_MSIX
+
# Ethernet
device ether
+device re
+device em
device mii
device bpf
options DEVICE_POLLING
Index: sys/boot/fdt/dts/arm/annapurna-alpine.dts
===================================================================
--- sys/boot/fdt/dts/arm/annapurna-alpine.dts
+++ sys/boot/fdt/dts/arm/annapurna-alpine.dts
@@ -164,11 +164,22 @@
};
};
+ /* MSIX Configuration */
+ msix {
+ compatible = "annapurna-labs,al-msix";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ reg = <0xfbe00000 0x100000>;
+ interrupts = <0 96 1 0 159 1>;
+ interrupt-parent = <&MPIC>;
+ };
+
pcie-internal {
compatible = "annapurna-labs,al-internal-pcie";
device_type = "pci";
#size-cells = <2>;
#address-cells = <3>;
+ reg = <0xfe000000 0x1000000>;
interrupt-parent = <&MPIC>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0x3000 0 0 1 &MPIC 0 32 4>, // USB adapter
Index: sys/conf/options.arm
===================================================================
--- sys/conf/options.arm
+++ sys/conf/options.arm
@@ -1,4 +1,6 @@
#$FreeBSD$
+ALPINE_PCI_MSI opt_alpine.h
+ALPINE_PCI_MSIX opt_alpine.h
ARMV6 opt_global.h
ARM_CACHE_LOCK_ENABLE opt_global.h
ARM_INTRNG opt_global.h
File Metadata
Details
Attached
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)
Attached To
Mode
D2579: PCI support for Alpine platform from Annapurna Labs
Attached
Detach File
Event Timeline
Log In to Comment