83c41143ca
driver would verify that requests for child devices were confined to any existing I/O windows, but the driver relied on the firmware to initialize the windows and would never grow the windows for new requests. Now the driver actively manages the I/O windows. This is implemented by allocating a bus resource for each I/O window from the parent PCI bus and suballocating that resource to child devices. The suballocations are managed by creating an rman for each I/O window. The suballocated resources are mapped by passing the bus_activate_resource() call up to the parent PCI bus. Windows are grown when needed by using bus_adjust_resource() to adjust the resource allocated from the parent PCI bus. If the adjust request succeeds, the window is adjusted and the suballocation request for the child device is retried. When growing a window, the rman_first_free_region() and rman_last_free_region() routines are used to determine if the front or end of the existing I/O window is free. From using that, the smallest ranges that need to be added to either the front or back of the window are computed. The driver will first try to grow the window in whichever direction requires the smallest growth first followed by the other direction if that fails. Subtractive bridges will first attempt to satisfy requests for child resources from I/O windows (including attempts to grow the windows). If that fails, the request is passed up to the parent PCI bus directly however. The PCI-PCI bridge driver will try to use firmware-assigned ranges for child BARs first and only allocate a "fresh" range if that specific range cannot be accommodated in the I/O window. This allows systems where the firmware assigns resources during boot but later wipes the I/O windows (some ACPI BIOSen are known to do this) to "rediscover" the original I/O window ranges. The ACPI Host-PCI bridge driver has been adjusted to correctly honor hw.acpi.host_mem_start and the I/O port equivalent when a PCI-PCI bridge makes a wildcard request for an I/O window range. The new PCI-PCI bridge driver is only enabled if the NEW_PCIB kernel option is enabled. This is a transition aide to allow platforms that do not yet support bus_activate_resource() and bus_adjust_resource() in their Host-PCI bridge drivers (and possibly other drivers as needed) to use the old driver for now. Once all platforms support the new driver, the kernel option and old driver will be removed. PR: kern/143874 kern/149306 Tested by: mav
261 lines
7.1 KiB
C
261 lines
7.1 KiB
C
/*-
|
|
* Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
|
|
* Copyright (c) 2000 Michael Smith <msmith@freebsd.org>
|
|
* Copyright (c) 2000 BSDi
|
|
* Copyright (c) 2001, 2003 Thomas Moestl <tmm@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* 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. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
|
|
*
|
|
* from: FreeBSD: src/sys/dev/pci/pci_pci.c,v 1.3 2000/12/13
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*
|
|
* Support for the Sun APB (Advanced PCI Bridge) PCI-PCI bridge.
|
|
* This bridge does not fully comply to the PCI bridge specification, and is
|
|
* therefore not supported by the generic driver.
|
|
* We can use some of the pcib methods anyway.
|
|
*/
|
|
|
|
#include "opt_ofw_pci.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <dev/ofw/ofw_bus.h>
|
|
#include <dev/ofw/openfirm.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcib_private.h>
|
|
|
|
#include "pcib_if.h"
|
|
|
|
#include <sparc64/pci/ofw_pci.h>
|
|
#include <sparc64/pci/ofw_pcib_subr.h>
|
|
|
|
/*
|
|
* Bridge-specific data.
|
|
*/
|
|
struct apb_softc {
|
|
struct ofw_pcib_gen_softc sc_bsc;
|
|
uint8_t sc_iomap;
|
|
uint8_t sc_memmap;
|
|
};
|
|
|
|
static device_probe_t apb_probe;
|
|
static device_attach_t apb_attach;
|
|
static bus_alloc_resource_t apb_alloc_resource;
|
|
|
|
static device_method_t apb_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, apb_probe),
|
|
DEVMETHOD(device_attach, apb_attach),
|
|
|
|
/* Bus interface */
|
|
DEVMETHOD(bus_alloc_resource, apb_alloc_resource),
|
|
|
|
/* pcib interface */
|
|
DEVMETHOD(pcib_route_interrupt, ofw_pcib_gen_route_interrupt),
|
|
|
|
/* ofw_bus interface */
|
|
DEVMETHOD(ofw_bus_get_node, ofw_pcib_gen_get_node),
|
|
|
|
KOBJMETHOD_END
|
|
};
|
|
|
|
static devclass_t pcib_devclass;
|
|
|
|
DEFINE_CLASS_1(pcib, apb_driver, apb_methods, sizeof(struct apb_softc),
|
|
pcib_driver);
|
|
EARLY_DRIVER_MODULE(apb, pci, apb_driver, pcib_devclass, 0, 0, BUS_PASS_BUS);
|
|
MODULE_DEPEND(apb, pci, 1, 1, 1);
|
|
|
|
/* APB specific registers */
|
|
#define APBR_IOMAP 0xde
|
|
#define APBR_MEMMAP 0xdf
|
|
|
|
/* Definitions for the mapping registers */
|
|
#define APB_IO_SCALE 0x200000
|
|
#define APB_MEM_SCALE 0x20000000
|
|
|
|
/*
|
|
* Generic device interface
|
|
*/
|
|
static int
|
|
apb_probe(device_t dev)
|
|
{
|
|
|
|
if (pci_get_vendor(dev) == 0x108e && /* Sun */
|
|
pci_get_device(dev) == 0x5000) { /* APB */
|
|
device_set_desc(dev, "APB PCI-PCI bridge");
|
|
return (0);
|
|
}
|
|
return (ENXIO);
|
|
}
|
|
|
|
static void
|
|
apb_map_print(uint8_t map, u_long scale)
|
|
{
|
|
int i, first;
|
|
|
|
for (first = 1, i = 0; i < 8; i++) {
|
|
if ((map & (1 << i)) != 0) {
|
|
printf("%s0x%lx-0x%lx", first ? "" : ", ",
|
|
i * scale, (i + 1) * scale - 1);
|
|
first = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
apb_checkrange(uint8_t map, u_long scale, u_long start, u_long end)
|
|
{
|
|
int i, ei;
|
|
|
|
i = start / scale;
|
|
ei = end / scale;
|
|
if (i > 7 || ei > 7)
|
|
return (0);
|
|
for (; i <= ei; i++)
|
|
if ((map & (1 << i)) == 0)
|
|
return (0);
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
apb_attach(device_t dev)
|
|
{
|
|
struct apb_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
/*
|
|
* Get current bridge configuration.
|
|
*/
|
|
sc->sc_bsc.ops_pcib_sc.domain = pci_get_domain(dev);
|
|
sc->sc_bsc.ops_pcib_sc.secbus =
|
|
pci_read_config(dev, PCIR_SECBUS_1, 1);
|
|
sc->sc_bsc.ops_pcib_sc.subbus =
|
|
pci_read_config(dev, PCIR_SUBBUS_1, 1);
|
|
sc->sc_iomap = pci_read_config(dev, APBR_IOMAP, 1);
|
|
sc->sc_memmap = pci_read_config(dev, APBR_MEMMAP, 1);
|
|
ofw_pcib_gen_setup(dev);
|
|
|
|
if (bootverbose) {
|
|
device_printf(dev, " domain %d\n",
|
|
sc->sc_bsc.ops_pcib_sc.domain);
|
|
device_printf(dev, " secondary bus %d\n",
|
|
sc->sc_bsc.ops_pcib_sc.secbus);
|
|
device_printf(dev, " subordinate bus %d\n",
|
|
sc->sc_bsc.ops_pcib_sc.subbus);
|
|
device_printf(dev, " I/O decode ");
|
|
apb_map_print(sc->sc_iomap, APB_IO_SCALE);
|
|
printf("\n");
|
|
device_printf(dev, " memory decode ");
|
|
apb_map_print(sc->sc_memmap, APB_MEM_SCALE);
|
|
printf("\n");
|
|
}
|
|
|
|
device_add_child(dev, "pci", -1);
|
|
return (bus_generic_attach(dev));
|
|
}
|
|
|
|
/*
|
|
* We have to trap resource allocation requests and ensure that the bridge
|
|
* is set up to, or capable of handling them.
|
|
*/
|
|
static struct resource *
|
|
apb_alloc_resource(device_t dev, device_t child, int type, int *rid,
|
|
u_long start, u_long end, u_long count, u_int flags)
|
|
{
|
|
struct apb_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
/*
|
|
* If this is a "default" allocation against this rid, we can't work
|
|
* out where it's coming from (we should actually never see these) so
|
|
* we just have to punt.
|
|
*/
|
|
if (start == 0 && end == ~0) {
|
|
device_printf(dev, "can't decode default resource id %d for "
|
|
"%s, bypassing\n", *rid, device_get_nameunit(child));
|
|
goto passup;
|
|
}
|
|
|
|
/*
|
|
* Fail the allocation for this range if it's not supported.
|
|
* XXX we should probably just fix up the bridge decode and
|
|
* soldier on.
|
|
*/
|
|
switch (type) {
|
|
case SYS_RES_IOPORT:
|
|
if (!apb_checkrange(sc->sc_iomap, APB_IO_SCALE, start, end)) {
|
|
device_printf(dev, "device %s requested unsupported "
|
|
"I/O range 0x%lx-0x%lx\n",
|
|
device_get_nameunit(child), start, end);
|
|
return (NULL);
|
|
}
|
|
if (bootverbose)
|
|
device_printf(sc->sc_bsc.ops_pcib_sc.dev, "device "
|
|
"%s requested decoded I/O range 0x%lx-0x%lx\n",
|
|
device_get_nameunit(child), start, end);
|
|
break;
|
|
|
|
case SYS_RES_MEMORY:
|
|
if (!apb_checkrange(sc->sc_memmap, APB_MEM_SCALE, start, end)) {
|
|
device_printf(dev, "device %s requested unsupported "
|
|
"memory range 0x%lx-0x%lx\n",
|
|
device_get_nameunit(child), start, end);
|
|
return (NULL);
|
|
}
|
|
if (bootverbose)
|
|
device_printf(sc->sc_bsc.ops_pcib_sc.dev, "device "
|
|
"%s requested decoded memory range 0x%lx-0x%lx\n",
|
|
device_get_nameunit(child), start, end);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
passup:
|
|
/*
|
|
* Bridge is OK decoding this resource, so pass it up.
|
|
*/
|
|
return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
|
|
count, flags));
|
|
}
|