Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F140120215
D2579.id10643.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
56 KB
Referenced Files
None
Subscribers
None
D2579.id10643.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 Annapurna Labs Ltd.
+
+This file may be licensed under the terms of the Annapurna Labs Commercial
+License Agreement.
+
+Alternatively, this file can be distributed under the terms of the GNU General
+Public License V2 as published by the Free Software Foundation and can be
+found at http://www.gnu.org/licenses/gpl-2.0.html
+
+Alternatively, redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#ifndef ALPINE_PCI_H_
+#define ALPINE_PCI_H_
+
+#define AL_PCI_RANGE_MAX 3
+#define AL_PCI_RANGE_BRIDGE 0
+#define AL_PCI_RANGE_MEM 1
+#define AL_PCI_RANGE_IO 2
+
+#define TARGET_BUS_MASK 0xFF
+
+int al_msix_map_msi(int irq, uint64_t *addr, uint32_t *data);
+int al_msix_alloc_msi(int count, int *irqs);
+int al_msix_release_msi(int count, int *irqs);
+
+#endif /* ALPINE_PCI_H_ */
Index: sys/arm/annapurna/alpine/alpine_pci.c
===================================================================
--- /dev/null
+++ sys/arm/annapurna/alpine/alpine_pci.c
@@ -0,0 +1,1617 @@
+/*-
+ * Copyright (c) 2008 MARVELL INTERNATIONAL LTD.
+ * Copyright (c) 2010 The FreeBSD Foundation
+ * Copyright (c) 2010-2015 Semihalf
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Portions of this software were developed by Semihalf
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Alpine PCI/PCI-Express controller driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/intr.h>
+#include <machine/fdt.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_pci.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include "ofw_bus_if.h"
+#include "pcib_if.h"
+#include "alpine_pci.h"
+#include "contrib/alpine-hal/al_hal_unit_adapter_regs.h"
+#include "contrib/alpine-hal/al_hal_pcie.h"
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include "opt_alpine.h"
+
+/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */
+#define PCI_MIN_IO_ALLOC 4
+#define PCI_MIN_MEM_ALLOC 16
+
+#define PCI_MAX_BUS_NUMBER 255
+
+#define PCIE_MIN_MPS 128
+#define PCIE_MAX_MPS 4096
+
+#define PCIE_MIN_MRRS 128
+#define PCIE_MAX_MRRS 4096
+
+#define AL_PCI_PORTS 4
+
+/* Maximum PCI/PCIE Memory */
+#define AL_PCI_MEM_SIZE (0x100000000)
+#define AL_PCI_MEM_SLICE_SIZE (AL_PCI_MEM_SIZE / AL_PCI_PORTS)
+
+/* Maximum PCI/PCIE I/O */
+#define AL_PCI_IO_SIZE (0x100000) /* 1MB */
+#define AL_PCI_IO_SLICE_SIZE (AL_PCI_IO_SIZE / AL_PCI_PORTS)
+
+#define AL_PCI_MAX_SLOTS 16
+#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t))
+#define AL_MAX_SUPPORTED_MPS 128
+
+#define AL_INTERNAL_BRIDGE_ECAM_BASE 0x2000
+
+#define PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS 5
+#define PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS 12
+
+#define FDT_RANGES_CELLS ((3 + 3 + 2) * 3)
+#define FDT_RANGE_MEM 0x02000000
+#define FDT_RANGE_IO 0x01000000
+
+#define AL_AXI_SLOT 15
+#define AL_AXI_FUNC 12
+
+#define TARGET_BUS_MASK 0xFF
+#define MAX_SUBORDINARY_BUS 0xFF
+
+#define CONFIG_ADDR_ZERO_BITS_MASK 0x3
+#define CONFIG_ADDR_BYTE_SELECTOR_MASK 0x3
+
+#define BAR_SIZE_IN_32BIT_UNITS(bar) (((bar & 7) == 4) ? 2 : 1)
+
+typedef enum {
+ AL_PCI_TYPE_INTERNAL = 1,
+ AL_PCI_TYPE_EXTERNAL = 2,
+} al_pci_type;
+
+static struct ofw_compat_data compat_data[] = {
+ {"annapurna-labs,al-internal-pcie", AL_PCI_TYPE_INTERNAL},
+ {"annapurna-labs,al-external-pcie", AL_PCI_TYPE_EXTERNAL},
+ {NULL, 0}
+};
+
+struct al_pci_range {
+ u_long base_pci;
+ u_long base_parent;
+ u_long len;
+};
+
+typedef struct {
+ device_t sc_dev;
+ al_pci_type sc_type;
+ uint32_t sc_busnr;
+ bus_addr_t ecam_handle;
+ struct resource *reg;
+ bus_space_tag_t reg_bst;
+ struct al_pcie_port pcie_port;
+ bus_addr_t reg_handle;
+ uint32_t index;
+ struct al_pcie_link_status status;
+ struct mtx conf_lock;
+ uint32_t target_bus;
+ struct al_pci_range pci_range[AL_PCI_RANGE_MAX];
+ struct rman sc_mem_rman;
+ struct rman sc_io_rman;
+ uint32_t sc_io_map[AL_PCI_IO_SLICE_SIZE /
+ (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)];
+ uint32_t sc_mem_map[AL_PCI_MEM_SLICE_SIZE /
+ (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)];
+ struct ofw_bus_iinfo sc_pci_iinfo;
+} al_pcib_softc;
+
+/* Forward prototypes */
+static int al_pcib_probe(device_t);
+static int al_pcib_attach(device_t);
+
+static struct resource *al_pcib_alloc_resource(device_t, device_t, int,
+ int *, u_long, u_long, u_long, u_int);
+static int al_pcib_release_resource(device_t, device_t, int, int,
+ struct resource *);
+static int al_pcib_read_ivar(device_t, device_t, int, uintptr_t *);
+static int al_pcib_write_ivar(device_t, device_t, int, uintptr_t);
+static int al_pcib_init(al_pcib_softc *, int, int);
+static void al_pcib_enable(al_pcib_softc *);
+static int al_pcib_write_ivar(device_t, device_t, int, uintptr_t);
+static int al_pcib_maxslots(device_t);
+static uint32_t al_pcib_read_config(device_t, u_int, u_int, u_int, u_int,
+ int);
+static void al_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
+ uint32_t, int);
+static int al_pcib_route_interrupt(device_t, device_t, int);
+static void al_pcib_fixup(al_pcib_softc *, int, int, int);
+static int al_pcib_mem_init(al_pcib_softc *);
+static uint32_t pcib_map_check(uint32_t *, uint32_t, uint32_t);
+static void pcib_map_set(uint32_t *, uint32_t, uint32_t);
+static bus_addr_t pcib_alloc(al_pcib_softc *, uint32_t);
+static int al_pcib_init_bar(al_pcib_softc *, int, int, int, int);
+static int al_pcib_init_all_bars(al_pcib_softc *, int, int, int, int);
+static void al_pcib_init_bridge(al_pcib_softc *, int, int, int);
+
+static int pcie_set_mps(al_pcib_softc *, int, int, int, int);
+static int pcie_get_mps(al_pcib_softc *, int, int, int);
+static uint16_t get_pcie_max_payload(al_pcib_softc *, int, int, int);
+static uint16_t get_pcie_get_type(al_pcib_softc *, int, int, int);
+static void pcie_write_mps(al_pcib_softc *, int, int, int, int);
+static uint16_t pcie_get_readrq(al_pcib_softc *, int, int, int);
+static int pcie_set_readrq(al_pcib_softc *, int, int, int, int);
+static void pcie_write_mrrs(al_pcib_softc *, int, int, int);
+static int pcie_bus_configure_set(al_pcib_softc *, int, int, int, uint8_t *);
+static int al_pcie_cfg_prepare(al_pcib_softc *);
+static int al_pcie_cfg_addr(device_t, u_int, u_int, u_int, u_int, int,
+ bus_addr_t *, bus_addr_t *);
+static int al_pcie_enable_controller(al_pcib_softc *);
+static int al_pcie_port_check_link(al_pcib_softc *);
+static int al_pcie_cfg_prepare(al_pcib_softc *);
+static int al_pcie_io_prepare(al_pcib_softc *);
+
+static int al_find_cap(device_t, u_int, u_int, u_int, int, int *);
+static int al_pci_ranges_decode(phandle_t,
+ struct al_pci_range *, struct al_pci_range *,
+ struct al_pci_range *, al_pcib_softc *);
+static int al_pcie_getproperty(phandle_t, pcell_t *, pcell_t *, pcell_t *,
+ pcell_t *);
+static int al_pcie_nranges(phandle_t, pcell_t *, pcell_t *, pcell_t *);
+
+#ifdef ALPINE_PCI_MSI
+static int al_pcib_map_msi(device_t, device_t, int, uint64_t *, uint32_t *);
+static int al_pcib_alloc_msi(device_t, device_t, int, int, int *);
+static int al_pcib_release_msi(device_t, device_t, int, int *);
+#endif
+#ifdef ALPINE_PCI_MSIX
+#ifndef ALPINE_PCI_MSI
+#error ALPINE_PCI_MSI must be defined prior enabling ALPINE_PCI_MSIX
+#endif
+static int al_pcib_alloc_msix(device_t, device_t, int *);
+static int al_pcib_release_msix(device_t, device_t, int);
+#endif
+/*
+ * Bus interface definitions.
+ */
+static device_method_t al_pcib_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, al_pcib_probe),
+ DEVMETHOD(device_attach, al_pcib_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, al_pcib_read_ivar),
+ DEVMETHOD(bus_write_ivar, al_pcib_write_ivar),
+ DEVMETHOD(bus_alloc_resource, al_pcib_alloc_resource),
+ DEVMETHOD(bus_release_resource, al_pcib_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, al_pcib_maxslots),
+ DEVMETHOD(pcib_read_config, al_pcib_read_config),
+ DEVMETHOD(pcib_write_config, al_pcib_write_config),
+ DEVMETHOD(pcib_route_interrupt, al_pcib_route_interrupt),
+
+#ifdef ALPINE_PCI_MSI
+ DEVMETHOD(pcib_alloc_msi, al_pcib_alloc_msi),
+ DEVMETHOD(pcib_release_msi, al_pcib_release_msi),
+ DEVMETHOD(pcib_map_msi, al_pcib_map_msi),
+#endif
+
+#ifdef ALPINE_PCI_MSIX
+ DEVMETHOD(pcib_alloc_msix, al_pcib_alloc_msix),
+ DEVMETHOD(pcib_release_msix, al_pcib_release_msix),
+#endif
+
+ /* OFW bus interface */
+ DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
+ DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
+ DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
+ DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
+ DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
+
+ DEVMETHOD_END
+};
+
+static driver_t al_pcib_driver = {
+ "pcib",
+ al_pcib_methods,
+ sizeof(al_pcib_softc),
+};
+
+devclass_t pcib_devclass;
+
+DRIVER_MODULE(pcib, ofwbus, al_pcib_driver, pcib_devclass, 0, 0);
+
+static int
+al_pcib_probe(device_t dev)
+{
+ al_pci_type pcit;
+ const struct ofw_compat_data *compat =
+ ofw_bus_search_compatible(dev, compat_data);
+
+ if (compat != NULL) {
+ pcit = compat->ocd_data;
+ switch (pcit) {
+ case AL_PCI_TYPE_INTERNAL:
+ device_set_desc(dev,
+ "Annapurna-Labs Integrated Internal PCI-E Controller");
+ return (BUS_PROBE_DEFAULT);
+ case AL_PCI_TYPE_EXTERNAL:
+ device_set_desc(dev,
+ "Annapurna-Labs Integrated External PCI-E Controller");
+ return (BUS_PROBE_DEFAULT);
+ default:
+ break;
+ }
+ }
+
+ return (ENXIO);
+}
+
+static int
+al_pcib_attach(device_t dev)
+{
+ al_pcib_softc *sc;
+ phandle_t node;
+ uint64_t reg_base = 0;
+ uint64_t reg_size = 0;
+ int ret = 0;
+ int rid;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ sc->sc_dev = dev;
+ sc->sc_busnr = device_get_unit(dev);
+ sc->index = device_get_unit(dev);
+ sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ mtx_init(&sc->conf_lock, "ALPCICFG", NULL, MTX_DEF);
+
+ rid = 0;
+ sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->reg == NULL) {
+ device_printf(dev, "Failed to allocate resource\n");
+ return (ENXIO);
+ }
+ sc->reg_bst = rman_get_bustag(sc->reg);
+
+ /* Map PCI regions */
+ ret = al_pci_ranges_decode(node, &sc->pci_range[AL_PCI_RANGE_BRIDGE],
+ &sc->pci_range[AL_PCI_RANGE_IO], &sc->pci_range[AL_PCI_RANGE_MEM],
+ sc);
+
+ if (ret != 0) {
+ device_printf(dev, "Decode PCI ranges failed, ret=%d\n", ret);
+ return (EFAULT);
+ }
+
+ if (sc->sc_type == AL_PCI_TYPE_EXTERNAL)
+ {
+ reg_base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci;
+ reg_size = sc->pci_range[AL_PCI_RANGE_MEM].len;
+
+ if (reg_size > 0)
+ {
+ ret = bus_space_map(sc->reg_bst, (bus_addr_t)reg_base,
+ (bus_size_t)reg_size, 0, &sc->reg_handle);
+ if (ret != 0) {
+ device_printf(dev, "Failed to map register space\n");
+ return (EFAULT);
+ }
+ }
+ }
+
+ /* Get PCI interrupt info. */
+ ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(pcell_t));
+
+ if (bootverbose) {
+ device_printf(dev,
+ "bridge space: base_parent = 0x%lx, base_pci = 0x%lx "
+ "len = %lx\n",
+ sc->pci_range[AL_PCI_RANGE_BRIDGE].base_parent,
+ sc->pci_range[AL_PCI_RANGE_BRIDGE].base_pci,
+ sc->pci_range[AL_PCI_RANGE_BRIDGE].len);
+ device_printf(dev,
+ "memory space: base_parent = 0x%lx, base_pci = 0x%lx "
+ "len = %lx\n", sc->pci_range[AL_PCI_RANGE_MEM].base_parent,
+ sc->pci_range[AL_PCI_RANGE_MEM].base_pci,
+ sc->pci_range[AL_PCI_RANGE_MEM].len);
+ device_printf(dev,
+ "IO space: base_parent = 0x%lx, base_pci = 0x%lx "
+ "len = %lx\n", sc->pci_range[AL_PCI_RANGE_IO].base_parent,
+ sc->pci_range[AL_PCI_RANGE_IO].base_pci,
+ sc->pci_range[AL_PCI_RANGE_IO].len);
+ }
+
+ if (bus_space_map(sc->reg_bst,
+ sc->pci_range[AL_PCI_RANGE_BRIDGE].base_parent,
+ sc->pci_range[AL_PCI_RANGE_BRIDGE].len, 0, &sc->ecam_handle)) {
+ device_printf(dev, "Failed to map bridge bus address\n");
+ return (EFAULT);
+ }
+
+ if (bootverbose)
+ device_printf(dev, "ecam_baddr = 0x%p\n",
+ (void*)sc->ecam_handle);
+
+ ret = al_pcie_enable_controller(sc);
+ if (ret != 0) {
+ device_printf(dev, "Failed to enable PCIE controller\n");
+ return (EFAULT);
+ }
+
+ ret = al_pcie_port_check_link(sc);
+ if (ret == 0) {
+ device_printf(dev, "Failed to check link\n");
+ return (EFAULT);
+ }
+
+ ret = al_pcie_cfg_prepare(sc);
+ if (ret != 0) {
+ device_printf(dev, "Failed to prepare config\n");
+ return (EFAULT);
+ }
+
+ ret = al_pcie_io_prepare(sc);
+ if (ret != 0) {
+ device_printf(dev, "Failed to prepare IO\n");
+ return (EFAULT);
+ }
+
+ /* Configure IOCC for external PCIE */
+ if (sc->sc_type != AL_PCI_TYPE_INTERNAL) {
+ al_pcie_port_snoop_config(&sc->pcie_port, 1);
+ al_pcib_enable(sc);
+ }
+
+ ret = al_pcib_mem_init(sc);
+ if (ret != 0) {
+ device_printf(dev, "Failed to al_pcib_mem_init\n");
+ return (EFAULT);
+ }
+
+ ret = al_pcib_init(sc, sc->sc_busnr,
+ al_pcib_maxslots(sc->sc_dev));
+ if (ret != 0)
+ return ret;
+
+ device_add_child(dev, "pci", -1);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+al_pci_ranges_decode(phandle_t node, struct al_pci_range *bridge_space,
+ struct al_pci_range *io_space, struct al_pci_range *mem_space,
+ al_pcib_softc *sc)
+{
+ pcell_t ranges[FDT_RANGES_CELLS];
+ struct al_pci_range *pci_space;
+ pcell_t addr_cells, size_cells, par_addr_cells;
+ pcell_t *rangesptr;
+ pcell_t cell0, cell1, cell2;
+ int tuples, i, rv, offset_cells;
+
+ if (al_pcie_getproperty(node, &par_addr_cells, &addr_cells,
+ &size_cells, ranges) != 0) {
+ device_printf(sc->sc_dev,
+ "Unexpected number of address or size cells in FDT "
+ " %d:%d:%d\n",
+ par_addr_cells, addr_cells, size_cells);
+ return (ENXIO);
+ }
+
+ tuples = al_pcie_nranges(node, &par_addr_cells, &addr_cells,
+ &size_cells);
+ if (tuples < 0)
+ return (ERANGE);
+
+ /*
+ * Initialize the ranges so that we don't have to worry about
+ * having them all defined in the FDT. In particular, it is
+ * perfectly fine not to want I/O space on PCI busses.
+ */
+ bzero(io_space, sizeof(*io_space));
+ bzero(mem_space, sizeof(*mem_space));
+ bzero(bridge_space, sizeof(*bridge_space));
+
+ rangesptr = &ranges[0];
+ offset_cells = 0;
+
+ for (i = 0; i < tuples; i++) {
+ cell0 = bswap32(*((uint32_t *)rangesptr));
+ rangesptr++;
+ cell1 = bswap32(*((uint32_t *)rangesptr));
+ rangesptr++;
+ cell2 = bswap32(*((uint32_t *)rangesptr));
+ rangesptr++;
+
+ if ((cell0 & FDT_RANGE_MEM) != 0) {
+ pci_space = mem_space;
+ } else if ((cell0 & FDT_RANGE_IO) != 0) {
+ pci_space = io_space;
+ } else if (cell0 == 0) {
+ pci_space = bridge_space;
+ } else {
+ rv = ERANGE;
+ goto out;
+ }
+
+ if (par_addr_cells == 3) {
+ /*
+ * This is a PCI subnode 'ranges'. Skip cell0 and
+ * cell1 of this entry and only use cell2.
+ */
+ offset_cells = 2;
+ rangesptr += offset_cells;
+ }
+
+ if ((par_addr_cells - offset_cells) == 1)
+ pci_space->base_parent =
+ bswap32(*((uint32_t *)rangesptr));
+ else
+ pci_space->base_parent =
+ bswap64(*((uint64_t *)rangesptr));
+
+ rangesptr += par_addr_cells - offset_cells;
+
+ if (size_cells == 1)
+ pci_space->len =
+ bswap32(*((uint32_t *)rangesptr));
+ else
+ pci_space->len =
+ bswap64(*((uint64_t *)rangesptr));
+
+ rangesptr += size_cells;
+ pci_space->base_pci = cell2;
+ }
+
+ rv = 0;
+out:
+ return (rv);
+}
+
+static int
+al_pcie_getproperty(phandle_t node, pcell_t *par_addr, pcell_t *pci_addr,
+ pcell_t *size, pcell_t *ranges)
+{
+
+ if (OF_getencprop(node, "#address-cells", pci_addr,
+ sizeof(*pci_addr)))
+ *pci_addr = 3;
+
+ if (OF_getencprop(node, "#size-cells", size,
+ sizeof(*size)))
+ *size = 2;
+
+ if (OF_getencprop(OF_parent(node), "#address-cells",
+ par_addr, sizeof(*par_addr)))
+ *par_addr = 1;
+
+ OF_getprop(node, "ranges", (void *)ranges,
+ FDT_RANGES_CELLS*sizeof(*ranges));
+
+ if ((*par_addr != 1) || (*pci_addr != 3) ||
+ (*size != 2))
+ return (ERANGE);
+
+ return (0);
+}
+
+static int
+al_pcie_nranges(phandle_t node, pcell_t *par_addr, pcell_t *pci_addr,
+ pcell_t *size)
+{
+ int len;
+
+ len = OF_getproplen(node, "ranges");
+ if (len <= 0)
+ return (-1);
+
+ return ((len / sizeof(cell_t)) /
+ (*pci_addr + *par_addr + *size));
+}
+
+static int
+al_pcie_enable_controller(al_pcib_softc *sc)
+{
+
+ if (sc->sc_type == AL_PCI_TYPE_INTERNAL)
+ return (0);
+
+ /* Initialize PCIe HAL with register tag and handle */
+ al_pcie_port_handle_init(&sc->pcie_port,
+ al_bus_dma_to_va(sc->reg_bst,
+ sc->reg_handle), NULL /* fixme */, sc->index);
+ if (al_pcie_operating_mode_get(&sc->pcie_port) !=
+ AL_PCIE_OPERATING_MODE_RC) {
+ device_printf(sc->sc_dev,
+ "controller is not configured to Root-Complex mode\n");
+ return (ENOSYS);
+ }
+
+ return (0);
+}
+
+/* Get ECAM address according to bus, device, function, and offset */
+static int
+al_pcie_cfg_addr(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
+ int bytes, bus_addr_t *handle, bus_addr_t *addr)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+ bus_addr_t ret_val = (bus_addr_t)NULL;
+
+ /* Trap out illegal values */
+ if (bus > PCI_MAX_BUS_NUMBER)
+ return (EFAULT);
+
+ ret_val = (bus_addr_t)(((slot << AL_AXI_SLOT) |
+ (func << AL_AXI_FUNC) | reg));
+ if (sc->sc_type == AL_PCI_TYPE_INTERNAL) {
+ *addr = ret_val;
+ *handle = sc->ecam_handle;
+ return (0);
+ }
+
+ /* Normalize bus number to the base of parent bridge */
+ bus -= sc->sc_busnr;
+
+ /* If there is no link, just show the PCI bridge. */
+ if (sc->status.link_up == AL_FALSE)
+ return (EFAULT);
+
+ if (slot > 0)
+ return (EFAULT);
+
+ if (bus == 0) {
+ ret_val = (bus_addr_t)(AL_INTERNAL_BRIDGE_ECAM_BASE + reg);
+ *handle = sc->reg_handle;
+ *addr = ret_val;
+ return (0);
+ }
+ /*
+ * Workaround: it enumerates on all internal bus numbers so
+ * communicate only with the first device on the bus
+ */
+ if (bus != sc->target_bus) {
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "change target bus number from %d to %d\n",
+ sc->target_bus, bus);
+ sc->target_bus = bus;
+ al_pcie_target_bus_set(&sc->pcie_port, sc->target_bus, TARGET_BUS_MASK);
+ }
+
+ *addr = ret_val;
+ *handle = sc->ecam_handle;
+ return (0);
+}
+
+static int
+al_pcie_port_check_link(al_pcib_softc *sc)
+{
+ struct al_pcie_link_status *status = &sc->status;
+ int rc;
+
+ if (sc->sc_type == AL_PCI_TYPE_INTERNAL)
+ return (1);
+
+ rc = al_pcie_link_status(&sc->pcie_port, status);
+
+ if (status->link_up == AL_FALSE) {
+ device_printf(sc->sc_dev, "link %u down\n", sc->index);
+ return (0);
+ }
+ device_printf(sc->sc_dev, "link up: speed Gen %d width x%x\n",
+ status->speed, status->lanes);
+
+ return (1);
+}
+
+/* prepare controller for issuing CFG transactions*/
+static int
+al_pcie_cfg_prepare(al_pcib_softc *sc)
+{
+
+ if (sc->sc_type == AL_PCI_TYPE_INTERNAL)
+ return (0);
+
+ sc->target_bus = 1;
+ /*
+ * force the controller to set the pci bus in the TLP to
+ * pcie->target_bus no matter what is the bus portion of the ECAM addess
+ * is.
+ */
+ al_pcie_target_bus_set(&sc->pcie_port, sc->target_bus, TARGET_BUS_MASK);
+
+ /* the bus connected to the controller always enumerated as bus 1*/
+ al_pcie_secondary_bus_set(&sc->pcie_port, 1);
+ /* set subordinary to max value */
+ al_pcie_subordinary_bus_set(&sc->pcie_port, MAX_SUBORDINARY_BUS);
+
+ return (0);
+}
+
+/* prepare controller for issuing IO transactions*/
+static int
+al_pcie_io_prepare(al_pcib_softc *sc)
+{
+ struct al_pcie_atu_region io_atu_region;
+
+ if (sc->sc_type == AL_PCI_TYPE_INTERNAL)
+ return (0);
+
+ io_atu_region.enable = AL_TRUE;
+ io_atu_region.direction = AL_PCIE_ATU_DIR_OUTBOUND;
+ io_atu_region.index = 0;
+ io_atu_region.base_addr = sc->pci_range[AL_PCI_RANGE_IO].base_parent;
+ io_atu_region.limit = sc->pci_range[AL_PCI_RANGE_IO].base_parent +
+ sc->pci_range[AL_PCI_RANGE_IO].len - 1;
+ /* the address that matches will be translated to this address + offset */
+ io_atu_region.target_addr = sc->pci_range[AL_PCI_RANGE_IO].base_pci;
+ io_atu_region.invert_matching = AL_FALSE;
+ io_atu_region.tlp_type = AL_PCIE_TLP_TYPE_IO; /* pcie tlp type*/
+ io_atu_region.attr = 0; /* pcie frame header attr field*/
+ /* outbound specific params */
+ io_atu_region.msg_code = 0; /* pcie message code */
+ io_atu_region.cfg_shift_mode = AL_FALSE;
+ /* inbound specific params*/
+ if (bootverbose) {
+ device_printf(sc->sc_dev,
+ "%s: base %llx, limit %llx, target %llx\n",
+ __func__, io_atu_region.base_addr,
+ io_atu_region.limit, io_atu_region.target_addr);
+ }
+ al_pcie_atu_region_set(&sc->pcie_port, &io_atu_region);
+
+ return (0);
+}
+
+static int
+al_pcib_mem_init(al_pcib_softc *sc)
+{
+ int err;
+
+ /*
+ * Memory management.
+ */
+ sc->sc_mem_rman.rm_type = RMAN_ARRAY;
+ err = rman_init(&sc->sc_mem_rman);
+ if (err != 0)
+ return (err);
+
+ sc->sc_io_rman.rm_type = RMAN_ARRAY;
+ err = rman_init(&sc->sc_io_rman);
+ if (err != 0) {
+ rman_fini(&sc->sc_mem_rman);
+ return (err);
+ }
+
+ err = rman_manage_region(&sc->sc_mem_rman,
+ sc->pci_range[AL_PCI_RANGE_MEM].base_pci,
+ sc->pci_range[AL_PCI_RANGE_MEM].base_pci +
+ sc->pci_range[AL_PCI_RANGE_MEM].len - 1);
+ if (err != 0)
+ goto error;
+
+ err = rman_manage_region(&sc->sc_io_rman,
+ sc->pci_range[AL_PCI_RANGE_IO].base_pci,
+ sc->pci_range[AL_PCI_RANGE_IO].base_pci +
+ sc->pci_range[AL_PCI_RANGE_IO].len - 1);
+ if (err != 0)
+ goto error;
+
+ return (0);
+
+error:
+ rman_fini(&sc->sc_mem_rman);
+ rman_fini(&sc->sc_io_rman);
+
+ return (err);
+}
+
+static inline uint32_t
+pcib_map_check(uint32_t *map, uint32_t start, uint32_t bits)
+{
+ uint32_t i;
+
+ for (i = start; i < start + bits; i++) {
+ if (isset(map, i))
+ return (0);
+ }
+
+ return (1);
+}
+
+static inline void
+pcib_map_set(uint32_t *map, uint32_t start, uint32_t bits)
+{
+ uint32_t i;
+
+ for (i = start; i < start + bits; i++)
+ setbit(map, i);
+}
+
+static bus_addr_t
+pcib_alloc(al_pcib_softc *sc, uint32_t smask)
+{
+ uint32_t bits, bits_limit, i, *map, min_alloc, size;
+ bus_addr_t addr = 0;
+ bus_addr_t base;
+
+ if (smask & 1) {
+ base = sc->pci_range[AL_PCI_RANGE_IO].base_pci;
+ min_alloc = PCI_MIN_IO_ALLOC;
+ bits_limit = sc->pci_range[AL_PCI_RANGE_IO].len / min_alloc;
+ map = sc->sc_io_map;
+ smask &= ~0x3;
+ } else {
+ base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci;
+ min_alloc = PCI_MIN_MEM_ALLOC;
+ bits_limit = sc->pci_range[AL_PCI_RANGE_MEM].len / min_alloc;
+ map = sc->sc_mem_map;
+ smask &= ~0xF;
+ }
+
+ size = ~smask + 1;
+ bits = size / min_alloc;
+
+ for (i = 0; i + bits <= bits_limit; i += bits) {
+ if (pcib_map_check(map, i, bits)) {
+ pcib_map_set(map, i, bits);
+ addr = base + (i * min_alloc);
+ goto end;
+ }
+ }
+
+end:
+ return (addr);
+}
+
+static int
+al_pcib_init_bar(al_pcib_softc *sc, int bus, int slot, int func,
+ int barno)
+{
+ uint32_t addr, bar;
+ int reg, width;
+
+ reg = PCIR_BAR(barno);
+
+ /*
+ * Need to init the BAR register with 0xffffffff before correct
+ * value can be read.
+ */
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, reg, 0xfffffffful, 4);
+ bar = al_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4);
+ if (bar == 0)
+ return (1);
+
+ /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */
+ width = BAR_SIZE_IN_32BIT_UNITS(bar);
+
+ addr = pcib_alloc(sc, bar);
+ if (addr == 0)
+ return (-1);
+
+ if (bootverbose)
+ printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n",
+ bus, slot, func, reg, bar, addr);
+
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4);
+ if (width == 2)
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4,
+ 0, 4);
+
+ return (width);
+}
+
+static int
+al_pcib_init_all_bars(al_pcib_softc *sc, int bus, int slot,
+ int func, int hdrtype)
+{
+ int maxbar, bar, i;
+
+ maxbar = (hdrtype & PCIM_HDRTYPE) != 0 ? 0 : 6;
+ bar = 0;
+
+ /* Program the base address registers */
+ while (bar < maxbar) {
+ i = al_pcib_init_bar(sc, bus, slot, func, bar);
+ bar += i;
+ if (i < 0) {
+ device_printf(sc->sc_dev,
+ "PCI IO/Memory space exhausted\n");
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static void
+al_pcib_init_bridge(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ bus_addr_t io_base, mem_base;
+ bus_size_t io_limit, mem_limit;
+ int secbus;
+
+ io_base = sc->pci_range[AL_PCI_RANGE_IO].base_pci;
+ io_limit = io_base + sc->pci_range[AL_PCI_RANGE_IO].len - 1;
+ mem_base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci;
+ mem_limit = mem_base + sc->pci_range[AL_PCI_RANGE_MEM].len - 1;
+
+ /* Configure I/O decode registers */
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1,
+ io_base >> 8, 1);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1,
+ io_base >> 16, 2);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1,
+ io_limit >> 8, 1);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1,
+ io_limit >> 16, 2);
+
+ /* Configure memory decode registers */
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1,
+ mem_base >> 16, 2);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1,
+ mem_limit >> 16, 2);
+
+ /* Disable memory prefetch decode */
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1,
+ 0x10, 2);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1,
+ 0x0, 4);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1,
+ 0xF, 2);
+ al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1,
+ 0x0, 4);
+
+ secbus = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+ PCIR_SECBUS_1, 1);
+
+ /* Configure buses behind the bridge */
+ al_pcib_init(sc, secbus, PCI_SLOTMAX);
+}
+
+static int
+al_find_cap(device_t child, u_int bus, u_int slot, u_int func,
+ int capability, int *capreg)
+{
+ uint32_t status;
+ uint8_t ptr;
+ uint16_t hdrtype;
+
+ /*
+ * Check the CAP_LIST bit of the PCI status register first.
+ */
+ status = al_pcib_read_config(child, bus, slot, func, PCIR_STATUS, 2);
+ if ((status & PCIM_STATUS_CAPPRESENT) != PCIM_STATUS_CAPPRESENT)
+ return (ENXIO);
+
+ hdrtype = al_pcib_read_config(child, bus, slot, func, PCIR_HDRTYPE, 1);
+
+ /*
+ * Determine the start pointer of the capabilities list.
+ */
+ switch (hdrtype & PCIM_HDRTYPE) {
+ case PCIM_HDRTYPE_NORMAL:
+ case PCIM_HDRTYPE_BRIDGE:
+ ptr = PCIR_CAP_PTR;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ ptr = PCIR_CAP_PTR_2;
+ break;
+ default:
+ device_printf(child, "al_find_cap: invalid hdrtype %x\n",
+ hdrtype & PCIM_HDRTYPE);
+ return (ENXIO); /* no extended capabilities support */
+ }
+ ptr = al_pcib_read_config(child, bus, slot, func, ptr, 1);
+
+ /*
+ * Traverse the capabilities list.
+ */
+ while (ptr != 0) {
+ if (al_pcib_read_config(child, bus, slot, func,
+ ptr + PCICAP_ID, 1) == capability) {
+ if (capreg != NULL)
+ *capreg = ptr;
+ return (0);
+ }
+ ptr = al_pcib_read_config(child, bus, slot, func,
+ ptr + PCICAP_NEXTPTR, 1);
+ }
+
+ return (ENOENT);
+}
+
+/**
+ * pcie_get_mps - get PCI Express maximum payload size configured
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ * or appropriate error value.
+ */
+static int
+pcie_get_mps(al_pcib_softc *sc, int bus, int slot, int func)
+{
+ uint16_t ctl;
+ int reg;
+
+ if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, ®) != 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,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+ struct rman *rm = NULL;
+ struct resource *res;
+ u_long bus_start = 0, bus_size = 0;
+
+ if (bootverbose)
+ device_printf(dev, "al_pcib_alloc_resource begin, start=0x%lx, "
+ "end=0x%lx, count=0x%lx, type=%d\n",
+ start, end, count, type);
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ rm = &sc->sc_io_rman;
+ bus_start = sc->pci_range[AL_PCI_RANGE_IO].base_pci;
+ bus_size = sc->pci_range[AL_PCI_RANGE_IO].len;
+ break;
+ case SYS_RES_MEMORY:
+ rm = &sc->sc_mem_rman;
+ bus_start = sc->pci_range[AL_PCI_RANGE_MEM].base_pci;
+ bus_size = sc->pci_range[AL_PCI_RANGE_MEM].len;
+ break;
+ default:
+ return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
+ type, rid, start, end, count, flags));
+ };
+
+ if ((start == 0UL) && (end == ~0UL)) {
+ start = bus_start;
+ end = bus_start + bus_size - 1;
+ count = bus_size;
+ }
+
+ if ((start < bus_start) || (start + count - 1 != end) ||
+ (end > bus_start + bus_size - 1)) {
+ device_printf(dev, "range failed\n");
+ return (NULL);
+ }
+
+ res = rman_reserve_resource(rm, start, end, count, flags, child);
+ if (res == NULL) {
+ device_printf(dev, "rman_reserve_resource failed\n");
+ return (NULL);
+ }
+
+ rman_set_rid(res, *rid);
+
+ if ((flags & RF_ACTIVE) != 0)
+ if (bus_activate_resource(child, type, *rid, res)) {
+ rman_release_resource(res);
+ device_printf(dev, "bus_activate_resource failed\n");
+ return (NULL);
+ }
+
+ return (res);
+}
+
+static int
+al_pcib_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+
+ switch (type) {
+ case SYS_RES_IOPORT:
+ case SYS_RES_MEMORY:
+ device_printf(dev, "Releasing SYS_RES_IOPORT and SYS_RES_MEMORY "
+ "resources is currently not supported. "
+ "This leads to memory leaks.\n");
+ break;
+ default:
+ return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
+ type, rid, res));
+ };
+
+ return (0);
+}
+
+static int
+al_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ *result = sc->sc_busnr;
+ return (0);
+ case PCIB_IVAR_DOMAIN:
+ *result = device_get_unit(dev);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static int
+al_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
+{
+ al_pcib_softc *sc = device_get_softc(dev);
+
+ switch (which) {
+ case PCIB_IVAR_BUS:
+ sc->sc_busnr = value;
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+static int
+al_pcib_maxslots(device_t dev)
+{
+
+ return (AL_PCI_MAX_SLOTS);
+}
+
+static uint32_t
+al_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, int bytes)
+{
+ bus_addr_t addr;
+ uint32_t val = 0xffffffff;
+ al_pcib_softc *sc = device_get_softc(dev);
+ bus_addr_t handle = 0;
+ int ret;
+
+ mtx_lock(&sc->conf_lock);
+
+ ret = al_pcie_cfg_addr(dev, bus, slot, func,
+ reg & ~CONFIG_ADDR_ZERO_BITS_MASK, bytes, &handle, &addr);
+
+ if (ret != 0)
+ goto end;
+
+ val = bus_space_read_4(sc->reg_bst, handle, addr);
+
+ switch (bytes) {
+ case 1:
+ val = (val >>
+ ((reg & CONFIG_ADDR_BYTE_SELECTOR_MASK) * NBBY)) & 0xff;
+ break;
+ case 2:
+ val = (val >>
+ ((reg & CONFIG_ADDR_BYTE_SELECTOR_MASK) * NBBY)) & 0xffff;
+ break;
+ default:
+ break;
+ }
+
+end:
+ mtx_unlock(&sc->conf_lock);
+
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "al_pcib_read_config begin addr=%p bus=%d, slot=%d, "
+ "func=%d reg=%d bytes=%d val=%x\n",
+ (void*)addr, bus, slot, func, reg, bytes, val);
+
+ return (val);
+}
+
+static void
+al_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
+ u_int reg, uint32_t val, int bytes)
+{
+ bus_addr_t addr;
+ al_pcib_softc *sc = device_get_softc(dev);
+ bus_addr_t handle = 0;
+ int ret;
+
+ mtx_lock(&sc->conf_lock);
+
+ ret = al_pcie_cfg_addr(dev, bus, slot, func, reg,
+ bytes, &handle, &addr);
+ if (ret != 0)
+ goto end;
+
+ switch (bytes) {
+ case 1:
+ bus_space_write_1(sc->reg_bst, handle, addr,
+ (uint8_t)val);
+ break;
+ case 2:
+ bus_space_write_2(sc->reg_bst, handle, addr,
+ (uint16_t)val);
+ break;
+ case 4:
+ bus_space_write_4(sc->reg_bst, handle, addr,
+ (uint32_t)val);
+ break;
+ default:
+ break;
+ }
+
+end:
+ mtx_unlock(&sc->conf_lock);
+
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "al_pcib_write_config begin addr=%p bus=%d, slot=%d, "
+ "func=%d reg=%d bytes=%d val=%x\n",
+ (void*)addr, bus, slot, func, reg, bytes, val);
+
+ return;
+}
+
+static int
+al_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+ al_pcib_softc *sc;
+ struct ofw_pci_register reg;
+ uint32_t pintr, mintr[4];
+ int icells;
+ phandle_t iparent;
+
+ sc = device_get_softc(pcib);
+ pintr = pin;
+
+ /* Fabricate imap information in case this isn't an OFW device */
+ bzero(®, sizeof(reg));
+ reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+ (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+ (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+ icells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo,
+ ®, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
+ &iparent);
+ if (icells > 0)
+ return (ofw_bus_map_intr(dev, iparent, icells, mintr));
+
+ /* Maybe it's a real interrupt, not an intpin */
+ if (pin > 4)
+ return (pin);
+
+ device_printf(pcib, "could not route pin %d for device %d.%d\n",
+ pin, pci_get_slot(dev), pci_get_function(dev));
+ return (PCI_INVALID_IRQ);
+}
+
+#ifdef ALPINE_PCI_MSI
+
+static int
+al_pcib_map_msi(device_t dev __unused, device_t child __unused, int irq,
+ uint64_t *addr, uint32_t *data)
+{
+
+ return (al_msix_map_msi(irq, addr, data));
+}
+
+static int
+al_pcib_alloc_msi(device_t dev __unused, device_t child __unused, int count,
+ int maxcount __unused, int *irqs)
+{
+
+ return (al_msix_alloc_msi(count, irqs));
+}
+
+static int
+al_pcib_release_msi(device_t dev __unused, device_t child __unused,
+ int count, int *irqs)
+{
+
+ return (al_msix_release_msi(count, irqs));
+}
+#endif
+
+#ifdef ALPINE_PCI_MSIX
+static int
+al_pcib_alloc_msix(device_t dev __unused, device_t child __unused, int *irq)
+{
+
+ return (al_msix_alloc_msi(1, irq));
+}
+
+static int
+al_pcib_release_msix(device_t dev __unused, device_t child __unused, int irq)
+{
+
+ return (al_msix_release_msi(1, &irq));
+}
+#endif
Index: sys/arm/annapurna/alpine/alpine_pci_msix.c
===================================================================
--- /dev/null
+++ sys/arm/annapurna/alpine/alpine_pci_msix.c
@@ -0,0 +1,336 @@
+/*-
+*******************************************************************************
+Copyright (C) 2015 Annapurna Labs Ltd.
+
+This file may be licensed under the terms of the Annapurna Labs Commercial
+License Agreement.
+
+Alternatively, this file can be distributed under the terms of the GNU General
+Public License V2 as published by the Free Software Foundation and can be
+found at http://www.gnu.org/licenses/gpl-2.0.html
+
+Alternatively, redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/intr.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "alpine_pci.h"
+#include "opt_alpine.h"
+
+#define MAX_MSIX_COUNT 128
+
+static int al_msix_attach(device_t);
+static int al_msix_probe(device_t);
+int al_msix_map_msi(int, uint64_t *, uint32_t *);
+int al_msix_alloc_msi(int, int *);
+int al_msix_release_msi(int, int *);
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t al_msix_methods[] = {
+ DEVMETHOD(device_probe, al_msix_probe),
+ DEVMETHOD(device_attach, al_msix_attach),
+
+ DEVMETHOD_END
+};
+
+struct al_msix_softc {
+ device_t sc_dev;
+ bus_addr_t base_addr;
+ struct resource *reg;
+ uint32_t irq_min;
+ uint32_t irq_max;
+ uint32_t irq_count;
+ struct mtx msi_mtx;
+ char msi_bitmap[(MAX_MSIX_COUNT-1)/sizeof(char) + 1];
+};
+
+static driver_t al_msix_driver = {
+ "al_msix",
+ al_msix_methods,
+ sizeof(struct al_msix_softc),
+};
+
+devclass_t al_msix_devclass;
+
+struct al_msix_softc *g_softc;
+
+DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, al_msix_devclass, 0, 0);
+
+static int
+al_msix_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "annapurna-labs,al-msix")) {
+ device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+al_msix_attach(device_t dev)
+{
+ struct al_msix_softc *sc;
+ struct resource_list rl;
+ struct resource_list_entry *rle;
+ phandle_t node;
+ uint64_t reg_base = 0;
+ uint64_t reg_size = 0;
+ int ret, rid;
+ int interrupts[2];
+ int interrupts_count = 0;
+ pcell_t reg[2];
+ pcell_t par_addr, par_size;
+
+ if (g_softc) {
+ device_printf(dev,
+ "Error, only one MSI-X controller is supported\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->reg == NULL) {
+ device_printf(dev, "Failed to allocate resource\n");
+ return (ENXIO);
+ }
+
+ OF_getencprop(OF_parent(node), "#address-cells",
+ &par_addr, sizeof(par_addr));
+
+ OF_getencprop(OF_parent(node), "#size-cells",
+ &par_size, sizeof(par_size));
+
+ OF_getprop(node, "reg", (void *)reg, sizeof(reg));
+
+ if (par_addr == 1)
+ reg_base = bswap32((uint32_t)reg[0]);
+ else
+ reg_base = bswap64((uint64_t)reg[0]);
+
+ if (par_size == 1)
+ reg_size = bswap32((uint32_t)reg[1]);
+ else
+ reg_size = bswap64((uint64_t)reg[1]);
+
+ sc->base_addr = (bus_addr_t)reg_base;
+
+ memset(&rl, 0, sizeof(rl));
+ resource_list_init(&rl);
+ ret = ofw_bus_intr_to_rl(dev, node, &rl, NULL);
+ if (ret) {
+ device_printf(dev, "Failed to get interrupt data\n");
+ return (ENXIO);
+ }
+
+ memset(interrupts, 0, sizeof(interrupts));
+ ret = 0;
+ STAILQ_FOREACH(rle, &rl, link) {
+ if (interrupts_count >=
+ ((sizeof(interrupts) / sizeof(interrupts[0])))) {
+ ret = ENOMEM;
+ break;
+ }
+ interrupts[interrupts_count] = rle->start;
+ interrupts_count++;
+ }
+ resource_list_free(&rl);
+ if (ret || (interrupts_count != 2)) {
+ device_printf(dev, "Unable to parse MSI-X IRQ assignment\n");
+ return (ENXIO);
+ }
+
+ sc->irq_min = interrupts[0];
+ sc->irq_max = interrupts[1];
+ sc->irq_count = (sc->irq_max - sc->irq_min + 1);
+
+ if (sc->irq_count > MAX_MSIX_COUNT)
+ sc->irq_max = sc->irq_min + MAX_MSIX_COUNT - 1;
+
+ mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
+
+ device_printf(dev,
+ "MSI-X base address 0x%llx, size 0x%llx, IRQ %d-%d\n",
+ reg_base, reg_size, sc->irq_min, sc->irq_max);
+
+ g_softc = sc;
+
+ return (bus_generic_attach(dev));
+}
+
+int
+al_msix_map_msi(int irq, uint64_t *addr, uint32_t *data)
+{
+ int ret;
+
+ /* Check if IRQ number is in range */
+ if ((irq > g_softc->irq_max) || (irq < g_softc->irq_min))
+ return (EINVAL);
+
+ /* validate parameters */
+ if (isclr(&g_softc->msi_bitmap, irq - g_softc->irq_min)) {
+ device_printf(g_softc->sc_dev, "invalid MSI 0x%x\n", irq);
+ return (EINVAL);
+ }
+
+ /*
+ * MSIX message address format:
+ * [63:20] - MSIx TBAR
+ * Same value as the MSIx Translation Base Address Register
+ * [19] - WFE_EXIT
+ * Once set by MSIx message, an EVENTI is signal to the CPUs
+ * cluster specified by ‘Local GIC Target List’
+ * [18:17] - Target GIC ID
+ * Specifies which IO-GIC (external shared GIC) is targeted
+ * 0: Local GIC, as specified by the Local GIC Target List
+ * 1: IO-GIC 0
+ * 2: Reserved
+ * 3: Reserved
+ * [16:13] - Local GIC Target List
+ * Specifies the Local GICs list targeted by this MSIx
+ * message.
+ * [16] If set, SPIn is set in Cluster 0 local GIC
+ * [15:13] Reserved
+ * [15] If set, SPIn is set in Cluster 1 local GIC
+ * [14] If set, SPIn is set in Cluster 2 local GIC
+ * [13] If set, SPIn is set in Cluster 3 local GIC
+ * [12:3] - SPIn
+ * Specifies the SPI (Shared Peripheral Interrupt) index to
+ * be set in target GICs
+ * Notes:
+ * If targeting any local GIC than only SPI[249:0] are valid
+ * [2] - Function vector
+ * MSI Data vector extension hint
+ * [1:0] - Reserved
+ * Must be set to zero
+ *
+ * The SPI offset is 32, so decrement IRQ number.
+ */
+ *addr = (uint64_t)g_softc->base_addr + (uint64_t)((1<<16) +
+ ((irq - 32) << 3));
+ *data = 0;
+
+ /* Controller expects to get positive-edge-triggered interrupt */
+ if ((arm_config_irq == NULL) || (ret = arm_config_irq(irq,
+ INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH))) {
+
+ device_printf(g_softc->sc_dev,
+ "unable to configure interrupt, aborting\n");
+ return (EINVAL);
+ }
+
+ device_printf(g_softc->sc_dev,
+ "MSI mapping: irq: %d addr: %jx data: %x\n",
+ irq, *addr, *data);
+
+ return (0);
+}
+
+int
+al_msix_alloc_msi(int count, int *irqs)
+{
+ u_int start = 0;
+ u_int i;
+
+ if ((powerof2(count) == 0) || (count > 8))
+ return (EINVAL);
+
+ mtx_lock(&g_softc->msi_mtx);
+
+ for (start = 0; (start + count) < (g_softc->irq_count); start++) {
+ for (i = start; i < start + count; i++) {
+ if (isset(&g_softc->msi_bitmap, i))
+ break;
+ }
+ if (i == (start + count))
+ break;
+ }
+
+ if ((start + count) >= g_softc->irq_max) {
+ mtx_unlock(&g_softc->msi_mtx);
+ return (ENXIO);
+ }
+
+ for (i = start; i < start + count; i++) {
+ setbit(&g_softc->msi_bitmap, i);
+ *irqs++ = g_softc->irq_min + i;
+ }
+
+ device_printf(g_softc->sc_dev,
+ "MSI-X allocation: start IRQ %d, count %d\n",
+ start + g_softc->irq_min, count);
+
+ mtx_unlock(&g_softc->msi_mtx);
+
+ return (0);
+}
+
+int
+al_msix_release_msi(int count, int *irqs)
+{
+ int i;
+
+ mtx_lock(&g_softc->msi_mtx);
+
+ for (i = 0; i < count; i++)
+ clrbit(&g_softc->msi_bitmap, irqs[i] - g_softc->irq_min);
+
+ mtx_unlock(&g_softc->msi_mtx);
+
+ return (0);
+}
Index: sys/arm/annapurna/alpine/files.alpine
===================================================================
--- sys/arm/annapurna/alpine/files.alpine
+++ sys/arm/annapurna/alpine/files.alpine
@@ -7,6 +7,9 @@
dev/uart/uart_dev_ns8250.c optional uart
dev/ofw/ofw_cpu.c standard
+arm/annapurna/alpine/alpine_pci.c optional pci
+arm/annapurna/alpine/alpine_pci_msix.c optional pci
+contrib/alpine-hal/al_hal_pcie.c optional pci
arm/annapurna/alpine/common.c standard
arm/annapurna/alpine/alpine_machdep.c standard
arm/annapurna/alpine/alpine_machdep_mp.c optional smp
Index: sys/arm/conf/ALPINE
===================================================================
--- sys/arm/conf/ALPINE
+++ sys/arm/conf/ALPINE
@@ -62,8 +62,15 @@
# Serial ports
device uart
+#PCI/PCIE
+device pci
+options ALPINE_PCI_MSI
+options ALPINE_PCI_MSIX
+
# Ethernet
device ether
+device re
+device em
device mii
device bpf
options DEVICE_POLLING
Index: sys/boot/fdt/dts/arm/annapurna-alpine.dts
===================================================================
--- sys/boot/fdt/dts/arm/annapurna-alpine.dts
+++ sys/boot/fdt/dts/arm/annapurna-alpine.dts
@@ -164,11 +164,22 @@
};
};
+ /* MSIX Configuration */
+ msix {
+ compatible = "annapurna-labs,al-msix";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ reg = <0xfbe00000 0x100000>;
+ interrupts = <0 96 1 0 159 1>;
+ interrupt-parent = <&MPIC>;
+ };
+
pcie-internal {
compatible = "annapurna-labs,al-internal-pcie";
device_type = "pci";
#size-cells = <2>;
#address-cells = <3>;
+ reg = <0xfe000000 0x1000000>;
interrupt-parent = <&MPIC>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <0x3000 0 0 1 &MPIC 0 32 4>, // USB adapter
Index: sys/conf/options.arm
===================================================================
--- sys/conf/options.arm
+++ sys/conf/options.arm
@@ -1,4 +1,6 @@
#$FreeBSD$
+ALPINE_PCI_MSI opt_alpine.h
+ALPINE_PCI_MSIX opt_alpine.h
ARMV6 opt_global.h
ARM_CACHE_LOCK_ENABLE opt_global.h
ARM_INTRNG opt_global.h
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 21, 12:30 PM (7 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27112903
Default Alt Text
D2579.id10643.diff (56 KB)
Attached To
Mode
D2579: PCI support for Alpine platform from Annapurna Labs
Attached
Detach File
Event Timeline
Log In to Comment