Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 May 2015 09:28:41 +0000
From:      "jpa-semihalf.com (Jakub Palider)" <phabric-noreply@FreeBSD.org>
To:        freebsd-arm@freebsd.org
Subject:   [Differential] [Request, 1, 557 lines] D2579: PCI support for Alpine platform from Annapurna Labs
Message-ID:  <differential-rev-PHID-DREV-y2vdtziki6lwmyrnc5ux-req@FreeBSD.org>

index | next in thread | raw e-mail

[-- Attachment #1 --]
jpa-semihalf.com created this revision.
jpa-semihalf.com added reviewers: andrew, ian, imp.
jpa-semihalf.com added a subscriber: freebsd-arm.

REVISION SUMMARY
  This is a continuation of https://reviews.freebsd.org/D2340 and follows Andrew's suggestion to extract code related to PCI support into a separate review. This is an intermediate step between the first commit and adding MSIx support (including transition towards Linux dts).

REVISION DETAIL
  https://reviews.freebsd.org/D2579

AFFECTED FILES
  sys/arm/annapurna/alpine/alpine_pci.c
  sys/arm/annapurna/alpine/alpine_pci.h
  sys/arm/annapurna/alpine/files.alpine
  sys/arm/conf/ALPINE

EMAIL PREFERENCES
  https://reviews.freebsd.org/settings/panel/emailpreferences/

To: jpa-semihalf.com, andrew, ian, imp
Cc: freebsd-arm

[-- Attachment #2 --]
diff --git a/sys/arm/conf/ALPINE b/sys/arm/conf/ALPINE
--- a/sys/arm/conf/ALPINE
+++ b/sys/arm/conf/ALPINE
@@ -63,8 +63,13 @@
 # Serial ports
 device		uart
 
+#PCI/PCIE
+device		pci
+
 # Ethernet
 device		ether
+device		re
+device		em
 device		mii
 device		bpf
 options 	DEVICE_POLLING
diff --git a/sys/arm/annapurna/alpine/files.alpine b/sys/arm/annapurna/alpine/files.alpine
--- a/sys/arm/annapurna/alpine/files.alpine
+++ b/sys/arm/annapurna/alpine/files.alpine
@@ -11,6 +11,8 @@
 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/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
diff --git a/sys/arm/annapurna/alpine/alpine_pci.h b/sys/arm/annapurna/alpine/alpine_pci.h
new file mode 100644
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_pci.h
@@ -0,0 +1,45 @@
+/*-
+********************************************************************************
+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
+
+#endif /* ALPINE_PCI_H_ */
diff --git a/sys/arm/annapurna/alpine/alpine_pci.c b/sys/arm/annapurna/alpine/alpine_pci.c
new file mode 100644
--- /dev/null
+++ b/sys/arm/annapurna/alpine/alpine_pci.c
@@ -0,0 +1,1505 @@
+/*-
+ * 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 "hal/al_hal_unit_adapter_regs.h"
+#include "hal/al_hal_pcie.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	CONFIG_ADDRESS_ZERO_BITS_MASK		0x3
+#define	CONFIG_ADDRESS_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 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 *sc, int bus, int maxslot);
+static void al_pcib_enable(al_pcib_softc *sc);
+static int al_pcib_write_ivar(device_t dev, device_t child, int which,
+    uintptr_t value);
+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 pcib, device_t dev, int pin);
+static void al_pcib_fixup(al_pcib_softc *sc, int bus, int slot, int func);
+static int al_pcib_mem_init(al_pcib_softc *sc);
+static uint32_t pcib_bit_get(uint32_t *map, uint32_t bit);
+static void pcib_bit_set(uint32_t *map, uint32_t bit);
+static uint32_t pcib_map_check(uint32_t *map, uint32_t start, uint32_t bits);
+static void pcib_map_set(uint32_t *map, uint32_t start, uint32_t bits);
+static bus_addr_t pcib_alloc(al_pcib_softc *sc, uint32_t smask);
+static int al_pcib_init_bar(al_pcib_softc *sc, int bus, int slot, int func,
+    int barno);
+static int al_pcib_init_all_bars(al_pcib_softc *sc, int bus, int slot,
+    int func, int hdrtype);
+static void al_pcib_init_bridge(al_pcib_softc *sc, int bus, int slot, int func);
+
+static int pcie_set_mps(al_pcib_softc *sc, int mps, int bus, int slot,
+    int func);
+static int pcie_get_mps(al_pcib_softc *sc, int bus, int slot, int func);
+static uint16_t get_pcie_max_payload(al_pcib_softc *sc, int bus, int slot,
+    int func);
+static uint16_t get_pcie_get_type(al_pcib_softc *sc, int bus, int slot,
+    int func);
+static void pcie_write_mps(al_pcib_softc *sc, int mps, int bus, int slot,
+    int func);
+static uint16_t pcie_get_readrq(al_pcib_softc *sc, int bus, int slot, int func);
+static int pcie_set_readrq(al_pcib_softc *sc, int rq, int bus, int slot,
+    int func);
+static void pcie_write_mrrs(al_pcib_softc *sc, int bus, int slot, int func);
+static int pcie_bus_configure_set(al_pcib_softc *sc, int bus, int slot,
+    int func, uint8_t *data);
+static int al_pcie_cfg_prepare(al_pcib_softc *sc);
+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);
+static int al_pcie_enable_controller(al_pcib_softc *sc);
+static int al_pcie_port_check_link(al_pcib_softc *sc);
+static int al_pcie_cfg_prepare(al_pcib_softc *sc);
+static int al_pcie_io_prepare(al_pcib_softc *sc);
+
+static int al_find_cap(device_t child, u_int bus, u_int slot, u_int func,
+    int capability, int *capreg);
+static int al_fdt_pci_ranges_decode(phandle_t node,
+    struct al_pci_range *bridge_space, struct al_pci_range *io_space,
+    struct al_pci_range *mem_space);
+/*
+ * 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_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
+
+	/* 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_fdt_pci_ranges_decode(phandle_t node, struct al_pci_range *bridge_space,
+    struct al_pci_range *io_space, struct al_pci_range *mem_space)
+{
+	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 tuple_size, tuples, i, rv, offset_cells, len;
+
+	/*
+	 * Retrieve 'ranges' property.
+	 */
+	if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
+		return (EINVAL);
+	if (addr_cells != 3 || size_cells != 2) {
+		return (ERANGE);
+	}
+
+	par_addr_cells = fdt_parent_addr_cells(node);
+	if (par_addr_cells > 3) {
+		return (ERANGE);
+	}
+
+	len = OF_getproplen(node, "ranges");
+	if (len > sizeof(ranges))
+		return (ENOMEM);
+
+	if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
+		return (EINVAL);
+
+	tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells +
+	    size_cells);
+	tuples = len / tuple_size;
+
+	/*
+	 * 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 = fdt_data_get((void *)rangesptr, 1);
+		rangesptr++;
+		cell1 = fdt_data_get((void *)rangesptr, 1);
+		rangesptr++;
+		cell2 = fdt_data_get((void *)rangesptr, 1);
+		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;
+		}
+
+		pci_space->base_parent = fdt_data_get((void *)rangesptr,
+		    par_addr_cells - offset_cells);
+		rangesptr += par_addr_cells - offset_cells;
+
+		pci_space->len = fdt_data_get((void *)rangesptr, size_cells);
+		rangesptr += size_cells;
+
+		pci_space->base_pci = cell2;
+	}
+	rv = 0;
+out:
+	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(fdtbus_bs_tag,
+	    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 (bus == 0) {
+		if (slot > 0) {
+			return (EFAULT);
+		}
+		ret_val = (bus_addr_t)(AL_INTERNAL_BRIDGE_ECAM_BASE + reg);
+		*handle = sc->reg_handle;
+		*addr = ret_val;
+		return (0);
+	} else {
+		/*
+		 * Workaround: it enumerates on all internal bus numbers so
+		 * communicate only with the first device on the bus
+		 */
+		if (slot > 0)
+			return (EFAULT);
+
+		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,
+			    0xFF);
+		}
+	}
+
+	*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 (rc < 0) {
+		device_printf(sc->sc_dev, "failed to get pcie link status\n");
+		return (0);
+	}
+
+	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, 0xFF);
+
+	/* 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, 0xff);
+
+	return (0);
+}
+
+static int
+al_pcib_probe(device_t dev)
+{
+	const char *dtype;
+	al_pci_type pcit;
+
+	dtype = ofw_bus_get_type(dev);
+	if (dtype == NULL || strcmp(dtype, "pci") != 0) {
+		return (ENXIO);
+	}
+
+	pcit = ofw_bus_search_compatible(dev, compat_data)->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);
+}
+
+
+/* prepare controller for issuing IO transactions*/
+static int
+al_pcie_io_prepare(al_pcib_softc *sc)
+{
+
+	if (sc->sc_type == AL_PCI_TYPE_INTERNAL)
+		return (0);
+
+	struct al_pcie_atu_region io_atu_region = {
+		.enable = AL_TRUE,
+		.direction = AL_PCIE_ATU_DIR_OUTBOUND,
+		.index = 0,
+		.base_addr = sc->pci_range[AL_PCI_RANGE_IO].base_parent,
+		.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 */
+		.target_addr = sc->pci_range[AL_PCI_RANGE_IO].base_pci,
+		.invert_matching = AL_FALSE,
+		.tlp_type = AL_PCIE_TLP_TYPE_IO, /* pcie tlp type*/
+		.attr = 0, /* pcie frame header attr field*/
+		/* outbound specific params */
+		.msg_code = 0, /* pcie message code */
+		.cfg_shift_mode = AL_FALSE,
+		/* inbound specific params*/
+	};
+
+	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)
+		return (err);
+
+	sc->sc_io_rman.rm_type = RMAN_ARRAY;
+	err = rman_init(&sc->sc_io_rman);
+	if (err) {
+		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)
+		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)
+		goto error;
+
+	return (0);
+
+error:
+	rman_fini(&sc->sc_mem_rman);
+	rman_fini(&sc->sc_io_rman);
+
+	return (err);
+}
+
+static inline uint32_t
+pcib_bit_get(uint32_t *map, uint32_t bit)
+{
+	uint32_t n = bit / BITS_PER_UINT32;
+
+	bit = bit % BITS_PER_UINT32;
+	return (map[n] & (1 << bit));
+}
+
+static inline void
+pcib_bit_set(uint32_t *map, uint32_t bit)
+{
+	uint32_t n = bit / BITS_PER_UINT32;
+
+	bit = bit % BITS_PER_UINT32;
+	map[n] |= (1 << bit);
+}
+
+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 (pcib_bit_get(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++)
+		pcib_bit_set(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;
+	uint32_t io_limit, mem_limit;
+	int secbus;
+
+	io_base = sc->pci_range[AL_PCI_RANGE_IO].base_pci;
+	io_limit = io_base + sc->pci_range[AL_PCI_RANGE_IO].len - 1;
+	mem_base = sc->pci_range[AL_PCI_RANGE_MEM].base_pci;
+	mem_limit = mem_base + sc->pci_range[AL_PCI_RANGE_MEM].len - 1;
+
+	/* Configure I/O decode registers */
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1,
+	    io_base >> 8, 1);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1,
+	    io_base >> 16, 2);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1,
+	    io_limit >> 8, 1);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1,
+	    io_limit >> 16, 2);
+
+	/* Configure memory decode registers */
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1,
+	    mem_base >> 16, 2);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1,
+	    mem_limit >> 16, 2);
+
+	/* Disable memory prefetch decode */
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1,
+	    0x10, 2);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1,
+	    0x0, 4);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1,
+	    0xF, 2);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1,
+	    0x0, 4);
+
+	secbus = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+	    PCIR_SECBUS_1, 1);
+
+	/* Configure buses behind the bridge */
+	al_pcib_init(sc, secbus, PCI_SLOTMAX);
+}
+
+static int
+al_find_cap(device_t child, u_int bus, u_int slot, u_int func,
+    int capability, int *capreg)
+{
+	uint32_t status;
+	uint8_t ptr;
+	uint16_t hdrtype;
+
+	/*
+	 * Check the CAP_LIST bit of the PCI status register first.
+	 */
+	status = al_pcib_read_config(child, bus, slot, func, PCIR_STATUS, 2);
+	if ((status & PCIM_STATUS_CAPPRESENT) != PCIM_STATUS_CAPPRESENT)
+		return (ENXIO);
+
+	hdrtype = al_pcib_read_config(child, bus, slot, func, PCIR_HDRTYPE, 1);
+
+	/*
+	 * Determine the start pointer of the capabilities list.
+	 */
+	switch (hdrtype & PCIM_HDRTYPE) {
+	case PCIM_HDRTYPE_NORMAL:
+	case PCIM_HDRTYPE_BRIDGE:
+		ptr = PCIR_CAP_PTR;
+		break;
+	case PCIM_HDRTYPE_CARDBUS:
+		ptr = PCIR_CAP_PTR_2;
+		break;
+	default:
+		device_printf(child, "al_find_cap: invalid hdrtype %x\n",
+		    hdrtype & PCIM_HDRTYPE);
+		return (ENXIO);		/* no extended capabilities support */
+	}
+	ptr = al_pcib_read_config(child, bus, slot, func, ptr, 1);
+
+	/*
+	 * Traverse the capabilities list.
+	 */
+	while (ptr != 0) {
+		if (al_pcib_read_config(child, bus, slot, func,
+		    ptr + PCICAP_ID, 1) == capability) {
+			if (capreg != NULL)
+				*capreg = ptr;
+			return (0);
+		}
+		ptr = al_pcib_read_config(child, bus, slot, func,
+		    ptr + PCICAP_NEXTPTR, 1);
+	}
+
+	return (ENOENT);
+}
+
+/**
+ * pcie_get_mps - get PCI Express maximum payload size configured
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ *    or appropriate error value.
+ */
+static int
+pcie_get_mps(al_pcib_softc *sc, int bus, int slot, int func)
+{
+	uint16_t ctl;
+	int reg;
+
+	if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, &reg) != 0) {
+		device_printf(sc->sc_dev, "Faield 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) >> 5));
+}
+
+/**
+ * get_pcie_max_payload - get PCI Express maximum supported payload size
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ *    or appropriate error value.
+ */
+static uint16_t
+get_pcie_max_payload(al_pcib_softc *sc, int bus, int slot, int func)
+{
+	uint16_t reg16;
+	int reg;
+
+	if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, &reg) != 0)
+		return (0);
+
+	reg16 = al_pcib_read_config(sc->sc_dev, bus, slot,
+	    func, reg + PCIER_DEVICE_CAP, 2);
+	return (reg16 & PCIEM_CAP_MAX_PAYLOAD);
+}
+
+/**
+ * get_pcie_get_type - get PCI Express device type (device or bridge)
+ * @dev: PCI device to query
+ *
+ * Returns maximum payload size in bytes
+ *    or appropriate error value.
+ */
+static uint16_t
+get_pcie_get_type(al_pcib_softc *sc, int bus, int slot, int func)
+{
+	uint16_t reg16;
+	int reg;
+
+	if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, &reg) != 0)
+		return (0);
+
+	reg16 = al_pcib_read_config(sc->sc_dev, bus, slot,
+	    func, reg + PCIER_FLAGS, 2);
+	return (reg16 & PCIEM_FLAGS_TYPE);
+}
+
+/**
+ * pcie_set_mps - set PCI Express maximum payload size
+ * @dev: PCI device to query
+ * @mps: maximum payload size in bytes
+ *    valid values are 128, 256, 512, 1024, 2048, 4096
+ *
+ * If possible sets maximum payload size
+ */
+static int
+pcie_set_mps(al_pcib_softc *sc, int mps, int bus, int slot, int func)
+{
+	uint16_t v;
+	int reg;
+	uint16_t tmp;
+
+	if (mps < PCIE_MIN_MPS || mps > PCIE_MAX_MPS ||
+	    !powerof2(mps))
+		return (EFAULT);
+
+	v = ffs(mps) - 8;
+	if (v > get_pcie_max_payload(sc, bus, slot, func))
+		return (EFAULT);
+	v <<= PCIEM_CTL_MAX_PAYLOAD_EMPTY_BITS;
+
+	if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, &reg) != 0)
+		return (EFAULT);
+
+	tmp = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+	    reg + PCIER_DEVICE_CTL, 2);
+	tmp &= ~PCIEM_CTL_MAX_PAYLOAD;
+	tmp |= (v & PCIEM_CTL_MAX_PAYLOAD);
+	al_pcib_write_config(sc->sc_dev, bus, slot, func,
+	    reg + PCIER_DEVICE_CTL, tmp, 2);
+
+	return (0);
+}
+
+static void
+pcie_write_mps(al_pcib_softc *sc, int mps, int bus, int slot, int func)
+{
+	int rc;
+	device_t parent;
+	uint16_t type;
+
+	mps = PCIE_MIN_MPS << get_pcie_max_payload(sc, bus, slot,
+	    func);
+
+	parent = device_get_parent(sc->sc_dev);
+	type = get_pcie_get_type(sc, bus, slot, func);
+
+	mps = min(mps, AL_MAX_SUPPORTED_MPS);
+
+	rc = pcie_set_mps(sc, mps, bus, slot, func);
+	if (rc != 0)
+		device_printf(sc->sc_dev,
+		    "Failed attempting to set the MPS %d\n", mps);
+}
+
+static uint16_t
+pcie_get_readrq(al_pcib_softc *sc, int bus, int slot, int func)
+{
+	uint16_t ctl;
+	int reg;
+
+	if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, &reg) != 0)
+		return (0);
+
+	ctl = al_pcib_read_config(sc->sc_dev, bus, slot, func,
+	    reg + PCIER_DEVICE_CTL, 2);
+
+	return (PCIE_MIN_MRRS << ((ctl & PCIEM_CTL_MAX_READ_REQUEST) >> 12));
+}
+
+/**
+ * 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;
+	int mps;
+	int reg;
+	uint16_t tmp;
+
+	if (rq < PCIE_MIN_MRRS || rq > PCIE_MAX_MRRS
+	    || !powerof2(rq))
+		return (EINVAL);
+
+	mps = pcie_get_mps(sc, bus, slot, func);
+
+	if (mps < 0)
+		return (mps);
+	if (mps < rq)
+		rq = mps;
+
+	v = (ffs(rq) - 8) << PCIEM_CTL_MAX_READ_REQUEST_EMPTY_BITS;
+
+	if (al_find_cap(sc->sc_dev,bus, slot, func, PCIY_EXPRESS, &reg) != 0)
+		return (EFAULT);
+
+	tmp = al_pcib_read_config(sc->sc_dev, bus, slot,
+	    func, reg + PCIER_DEVICE_CTL, 2);
+	tmp &= ~PCIEM_CTL_MAX_READ_REQUEST;
+	tmp |= (v & PCIEM_CTL_MAX_READ_REQUEST);
+	al_pcib_write_config(sc->sc_dev, bus, slot,
+	    func, reg + PCIER_DEVICE_CTL, tmp, 2);
+
+	return (0);
+}
+
+static void
+pcie_write_mrrs(al_pcib_softc *sc, int bus, int slot, int func)
+{
+	int rc, mrrs;
+
+	/* For Max performance, the MRRS must be set to the largest supported
+	 * value.  However, it cannot be configured larger than the MPS the
+	 * device or the bus can support.  This should already be properly
+	 * configured by a prior call to pcie_write_mps.
+	 */
+	mrrs = pcie_get_mps(sc, bus, slot, func);
+
+	/* MRRS is a R/W register.  Invalid values can be written, but a
+	 * subsequent read will verify if the value is acceptable or not.
+	 * If the MRRS value provided is not acceptable (e.g., too large),
+	 * shrink the value until it is acceptable to the HW.
+	 */
+	while (mrrs != pcie_get_readrq(sc, bus, slot, func) && mrrs >= PCIE_MIN_MRRS) {
+		rc = pcie_set_readrq(sc, mrrs, bus, slot, func);
+		if (!rc)
+			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)
+{
+	uint8_t smpss = 0;
+	uint32_t val;
+	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;
+	uint8_t hdrtype, command, class, subclass;
+	int 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))
+				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 int
+al_pcib_attach(device_t dev)
+{
+	al_pcib_softc	*sc;
+	phandle_t	node;
+	int		ret;
+	uint64_t	reg_base;
+	uint64_t	reg_size;
+
+	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);
+
+	/* Map register space */
+	reg_base = 0;
+	reg_size = 0;
+	ret = fdt_regsize(node, (u_long*)&reg_base, (u_long*)&reg_size);
+	if (ret == 0 && reg_size > 0) {
+		device_printf(dev, "register base = 0x%llx, size = 0x%lx\n",
+		    reg_base, (u_long)reg_size);
+		ret = bus_space_map(fdtbus_bs_tag, (bus_addr_t)reg_base,
+		    (bus_size_t)reg_size, 0, &sc->reg_handle);
+		if (ret) {
+			device_printf(dev, "Failed to map register space\n");
+			return (EFAULT);
+		}
+	}
+
+	/* Map PCI regions */
+	ret = al_fdt_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]);
+	if (ret) {
+		device_printf(dev, "Decode PCI ranges failed, ret=%d\n", ret);
+		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(fdtbus_bs_tag,
+	    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) {
+		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) {
+		device_printf(dev, "Failed to prepare config\n");
+		return (EFAULT);
+	}
+
+	ret = al_pcie_io_prepare(sc);
+	if (ret) {
+		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) {
+		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)
+		return ret;
+
+	device_add_child(dev, "pci", -1);
+
+	return (bus_generic_attach(dev));
+}
+
+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);
+	rman_set_bustag(res, fdtbus_bs_tag);
+	rman_set_bushandle(res, start);
+
+	if (flags & RF_ACTIVE)
+		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)
+{
+
+	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_ADDRESS_ZERO_BITS_MASK, bytes, &handle, &addr);
+
+	if (ret)
+		goto end;
+
+	val = bus_space_read_4(fdtbus_bs_tag, handle, addr);
+	switch (bytes) {
+	case 1:
+		val = (val >> ((reg & CONFIG_ADDRESS_BYTE_SELECTOR_MASK) * NBBY)) & 0xff;
+		break;
+	case 2:
+		val = (val >> ((reg & CONFIG_ADDRESS_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)
+		goto end;
+
+	switch (bytes) {
+	case 1:
+		bus_space_write_1(fdtbus_bs_tag, handle, addr,
+		    (uint8_t)val);
+		break;
+	case 2:
+		bus_space_write_2(fdtbus_bs_tag, handle, addr,
+		    (uint16_t)val);
+		break;
+	case 4:
+		bus_space_write_4(fdtbus_bs_tag, handle, addr,
+		    (uint32_t)val);
+		break;
+	default:
+		break;
+	}
+
+end:
+	mtx_unlock(&sc->conf_lock);
+
+	if (bootverbose)
+		device_printf(sc->sc_dev,
+		    "al_pcib_write_config begin addr=%p bus=%d, slot=%d, "
+		    "func=%d reg=%d bytes=%d val=%x\n",
+		    (void*)addr, bus, slot, func, reg, bytes, val);
+
+	return;
+}
+
+static int
+al_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+	al_pcib_softc *sc;
+	struct ofw_pci_register reg;
+	uint32_t pintr, mintr[4];
+	int icells;
+	phandle_t iparent;
+
+	sc = device_get_softc(pcib);
+	pintr = pin;
+
+	/* Fabricate imap information in case this isn't an OFW device */
+	bzero(&reg, sizeof(reg));
+	reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) |
+	    (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
+	    (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
+
+	icells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo,
+	    &reg, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
+	    &iparent);
+	if (icells > 0)
+		return (ofw_bus_map_intr(dev, iparent, icells, mintr));
+
+	/* Maybe it's a real interrupt, not an intpin */
+	if (pin > 4)
+		return (pin);
+
+	device_printf(pcib, "could not route pin %d for device %d.%d\n",
+	    pin, pci_get_slot(dev), pci_get_function(dev));
+	return (PCI_INVALID_IRQ);
+}
+

help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?differential-rev-PHID-DREV-y2vdtziki6lwmyrnc5ux-req>