mpc85xx: Import from FreeBSD

This commit is contained in:
Sebastian Huber 2021-12-15 09:53:25 +01:00 committed by Christian Mauderer
parent 3c9c9f19ba
commit 1e81e38a30
12 changed files with 4269 additions and 0 deletions

View File

@ -0,0 +1,677 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* Copyright (c) 2011 Nathan Whitehorn
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/rman.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/ofwpci.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcib_private.h>
#include <machine/bus.h>
#include <machine/md_var.h>
#include <machine/resource.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <rtems/bsd/local/pcib_if.h>
/*
* If it is necessary to set another value of this for
* some platforms it should be set at fdt.h file
*/
#ifndef PCI_MAP_INTR
#define PCI_MAP_INTR 4
#endif
#define PCI_INTR_PINS 4
/*
* bus interface.
*/
static struct resource * ofw_pci_alloc_resource(device_t, device_t,
int, int *, rman_res_t, rman_res_t, rman_res_t, u_int);
static int ofw_pci_release_resource(device_t, device_t, int, int,
struct resource *);
static int ofw_pci_activate_resource(device_t, device_t, int, int,
struct resource *);
static int ofw_pci_deactivate_resource(device_t, device_t, int, int,
struct resource *);
static int ofw_pci_adjust_resource(device_t, device_t, int,
struct resource *, rman_res_t, rman_res_t);
#ifdef __powerpc__
static bus_space_tag_t ofw_pci_bus_get_bus_tag(device_t, device_t);
#endif
/*
* pcib interface
*/
static int ofw_pci_maxslots(device_t);
/*
* ofw_bus interface
*/
static phandle_t ofw_pci_get_node(device_t, device_t);
/*
* local methods
*/
static int ofw_pci_fill_ranges(phandle_t, struct ofw_pci_range *);
static struct rman *ofw_pci_get_rman(struct ofw_pci_softc *, int, u_int);
/*
* Driver methods.
*/
static device_method_t ofw_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_attach, ofw_pci_attach),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar),
DEVMETHOD(bus_write_ivar, ofw_pci_write_ivar),
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource),
DEVMETHOD(bus_release_resource, ofw_pci_release_resource),
DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource),
DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource),
DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource),
#ifdef __powerpc__
DEVMETHOD(bus_get_bus_tag, ofw_pci_bus_get_bus_tag),
#endif
/* pcib interface */
DEVMETHOD(pcib_maxslots, ofw_pci_maxslots),
DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt),
DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, ofw_pci_get_node),
DEVMETHOD_END
};
DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0);
int
ofw_pci_init(device_t dev)
{
struct ofw_pci_softc *sc;
phandle_t node;
u_int32_t busrange[2];
struct ofw_pci_range *rp;
int i, error;
struct ofw_pci_cell_info *cell_info;
node = ofw_bus_get_node(dev);
sc = device_get_softc(dev);
sc->sc_initialized = 1;
sc->sc_range = NULL;
sc->sc_pci_domain = device_get_unit(dev);
cell_info = (struct ofw_pci_cell_info *)malloc(sizeof(*cell_info),
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_cell_info = cell_info;
if (OF_getencprop(node, "bus-range", busrange, sizeof(busrange)) != 8)
busrange[0] = 0;
sc->sc_dev = dev;
sc->sc_node = node;
sc->sc_bus = busrange[0];
if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) {
phandle_t c;
int n, i;
sc->sc_nrange = 0;
for (c = OF_child(node); c != 0; c = OF_peer(c)) {
n = ofw_pci_nranges(c, cell_info);
if (n > 0)
sc->sc_nrange += n;
}
if (sc->sc_nrange == 0) {
error = ENXIO;
goto out;
}
sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]),
M_DEVBUF, M_WAITOK);
i = 0;
for (c = OF_child(node); c != 0; c = OF_peer(c)) {
n = ofw_pci_fill_ranges(c, &sc->sc_range[i]);
if (n > 0)
i += n;
}
KASSERT(i == sc->sc_nrange, ("range count mismatch"));
} else {
sc->sc_nrange = ofw_pci_nranges(node, cell_info);
if (sc->sc_nrange <= 0) {
device_printf(dev, "could not getranges\n");
error = ENXIO;
goto out;
}
sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]),
M_DEVBUF, M_WAITOK);
ofw_pci_fill_ranges(node, sc->sc_range);
}
sc->sc_io_rman.rm_type = RMAN_ARRAY;
sc->sc_io_rman.rm_descr = "PCI I/O Ports";
error = rman_init(&sc->sc_io_rman);
if (error != 0) {
device_printf(dev, "rman_init() failed. error = %d\n", error);
goto out;
}
sc->sc_mem_rman.rm_type = RMAN_ARRAY;
sc->sc_mem_rman.rm_descr = "PCI Non Prefetchable Memory";
error = rman_init(&sc->sc_mem_rman);
if (error != 0) {
device_printf(dev, "rman_init() failed. error = %d\n", error);
goto out;
}
sc->sc_pmem_rman.rm_type = RMAN_ARRAY;
sc->sc_pmem_rman.rm_descr = "PCI Prefetchable Memory";
error = rman_init(&sc->sc_pmem_rman);
if (error != 0) {
device_printf(dev, "rman_init() failed. error = %d\n", error);
goto out;
}
for (i = 0; i < sc->sc_nrange; i++) {
error = 0;
rp = sc->sc_range + i;
if (sc->sc_range_mask & ((uint64_t)1 << i))
continue;
switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
case OFW_PCI_PHYS_HI_SPACE_CONFIG:
break;
case OFW_PCI_PHYS_HI_SPACE_IO:
error = rman_manage_region(&sc->sc_io_rman, rp->pci,
rp->pci + rp->size - 1);
break;
case OFW_PCI_PHYS_HI_SPACE_MEM32:
case OFW_PCI_PHYS_HI_SPACE_MEM64:
if (rp->pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) {
sc->sc_have_pmem = 1;
error = rman_manage_region(&sc->sc_pmem_rman,
rp->pci, rp->pci + rp->size - 1);
} else {
error = rman_manage_region(&sc->sc_mem_rman,
rp->pci, rp->pci + rp->size - 1);
}
break;
}
if (error != 0) {
device_printf(dev,
"rman_manage_region(%x, %#jx, %#jx) failed. "
"error = %d\n", rp->pci_hi &
OFW_PCI_PHYS_HI_SPACEMASK, rp->pci,
rp->pci + rp->size - 1, error);
goto out;
}
}
ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t));
return (0);
out:
free(cell_info, M_DEVBUF);
free(sc->sc_range, M_DEVBUF);
rman_fini(&sc->sc_io_rman);
rman_fini(&sc->sc_mem_rman);
rman_fini(&sc->sc_pmem_rman);
return (error);
}
int
ofw_pci_attach(device_t dev)
{
struct ofw_pci_softc *sc;
int error;
sc = device_get_softc(dev);
if (!sc->sc_initialized) {
error = ofw_pci_init(dev);
if (error != 0)
return (error);
}
device_add_child(dev, "pci", -1);
return (bus_generic_attach(dev));
}
static int
ofw_pci_maxslots(device_t dev)
{
return (PCI_SLOTMAX);
}
int
ofw_pci_route_interrupt(device_t bus, device_t dev, int pin)
{
struct ofw_pci_softc *sc;
struct ofw_pci_register reg;
uint32_t pintr, mintr[PCI_MAP_INTR];
int intrcells;
phandle_t iparent;
sc = device_get_softc(bus);
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);
intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev),
&sc->sc_pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr),
mintr, sizeof(mintr), &iparent);
if (intrcells != 0) {
pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr);
return (pintr);
}
/*
* Maybe it's a real interrupt, not an intpin
*/
if (pin > PCI_INTR_PINS)
return (pin);
device_printf(bus, "could not route pin %d for device %d.%d\n",
pin, pci_get_slot(dev), pci_get_function(dev));
return (PCI_INVALID_IRQ);
}
int
ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
{
struct ofw_pci_softc *sc;
sc = device_get_softc(dev);
switch (which) {
case PCIB_IVAR_DOMAIN:
*result = sc->sc_pci_domain;
return (0);
case PCIB_IVAR_BUS:
*result = sc->sc_bus;
return (0);
default:
break;
}
return (ENOENT);
}
int
ofw_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
{
struct ofw_pci_softc *sc;
sc = device_get_softc(dev);
switch (which) {
case PCIB_IVAR_BUS:
sc->sc_bus = value;
return (0);
default:
break;
}
return (ENOENT);
}
int
ofw_pci_nranges(phandle_t node, struct ofw_pci_cell_info *info)
{
ssize_t nbase_ranges;
if (info == NULL)
return (-1);
info->host_address_cells = 1;
info->size_cells = 2;
info->pci_address_cell = 3;
OF_getencprop(OF_parent(node), "#address-cells",
&(info->host_address_cells), sizeof(info->host_address_cells));
OF_getencprop(node, "#address-cells",
&(info->pci_address_cell), sizeof(info->pci_address_cell));
OF_getencprop(node, "#size-cells", &(info->size_cells),
sizeof(info->size_cells));
nbase_ranges = OF_getproplen(node, "ranges");
if (nbase_ranges <= 0)
return (-1);
return (nbase_ranges / sizeof(cell_t) /
(info->pci_address_cell + info->host_address_cells +
info->size_cells));
}
static struct resource *
ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct ofw_pci_softc *sc;
struct resource *rv;
struct rman *rm;
int needactivate;
needactivate = flags & RF_ACTIVE;
flags &= ~RF_ACTIVE;
sc = device_get_softc(bus);
#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
if (type == PCI_RES_BUS) {
return (pci_domain_alloc_bus(sc->sc_pci_domain, child, rid,
start, end, count, flags | needactivate));
}
#endif
rm = ofw_pci_get_rman(sc, type, flags);
if (rm == NULL) {
return (bus_generic_alloc_resource(bus, child, type, rid,
start, end, count, flags | needactivate));
}
rv = rman_reserve_resource(rm, start, end, count, flags, child);
if (rv == NULL) {
device_printf(bus, "failed to reserve resource for %s\n",
device_get_nameunit(child));
return (NULL);
}
rman_set_rid(rv, *rid);
if (needactivate) {
if (bus_activate_resource(child, type, *rid, rv) != 0) {
device_printf(bus,
"failed to activate resource for %s\n",
device_get_nameunit(child));
rman_release_resource(rv);
return (NULL);
}
}
return (rv);
}
static int
ofw_pci_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *res)
{
struct ofw_pci_softc *sc;
struct rman *rm;
int error;
sc = device_get_softc(bus);
#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
if (type == PCI_RES_BUS)
return (pci_domain_release_bus(sc->sc_pci_domain, child, rid,
res));
#endif
rm = ofw_pci_get_rman(sc, type, rman_get_flags(res));
if (rm == NULL) {
return (bus_generic_release_resource(bus, child, type, rid,
res));
}
KASSERT(rman_is_region_manager(res, rm), ("rman mismatch"));
if (rman_get_flags(res) & RF_ACTIVE) {
error = bus_deactivate_resource(child, type, rid, res);
if (error != 0)
return (error);
}
return (rman_release_resource(res));
}
static int
ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid,
struct resource *res)
{
struct ofw_pci_softc *sc;
bus_space_handle_t handle;
bus_space_tag_t tag;
struct ofw_pci_range *rp;
vm_paddr_t start;
int space;
int rv;
sc = device_get_softc(bus);
if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) {
return (bus_generic_activate_resource(bus, child, type, rid,
res));
}
start = (vm_paddr_t)rman_get_start(res);
/*
* Map this through the ranges list
*/
for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange &&
rp->pci_hi != 0; rp++) {
if (start < rp->pci || start >= rp->pci + rp->size)
continue;
switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
case OFW_PCI_PHYS_HI_SPACE_IO:
space = SYS_RES_IOPORT;
break;
case OFW_PCI_PHYS_HI_SPACE_MEM32:
case OFW_PCI_PHYS_HI_SPACE_MEM64:
space = SYS_RES_MEMORY;
break;
default:
space = -1;
}
if (type == space) {
start += (rp->host - rp->pci);
break;
}
}
if (bootverbose)
printf("ofw_pci mapdev: start %jx, len %jd\n",
(rman_res_t)start, rman_get_size(res));
tag = BUS_GET_BUS_TAG(child, child);
if (tag == NULL)
return (ENOMEM);
rman_set_bustag(res, tag);
rv = bus_space_map(tag, start,
rman_get_size(res), 0, &handle);
if (rv != 0)
return (ENOMEM);
rman_set_bushandle(res, handle);
rman_set_virtual(res, (void *)handle); /* XXX for powerpc only ? */
return (rman_activate_resource(res));
}
#ifdef __powerpc__
static bus_space_tag_t
ofw_pci_bus_get_bus_tag(device_t bus, device_t child)
{
return (&bs_le_tag);
}
#endif
static int
ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid,
struct resource *res)
{
vm_size_t psize;
if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) {
return (bus_generic_deactivate_resource(bus, child, type, rid,
res));
}
psize = rman_get_size(res);
pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
return (rman_deactivate_resource(res));
}
static int
ofw_pci_adjust_resource(device_t bus, device_t child, int type,
struct resource *res, rman_res_t start, rman_res_t end)
{
struct rman *rm;
struct ofw_pci_softc *sc;
sc = device_get_softc(bus);
#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
if (type == PCI_RES_BUS)
return (pci_domain_adjust_bus(sc->sc_pci_domain, child, res,
start, end));
#endif
rm = ofw_pci_get_rman(sc, type, rman_get_flags(res));
if (rm == NULL) {
return (bus_generic_adjust_resource(bus, child, type, res,
start, end));
}
KASSERT(rman_is_region_manager(res, rm), ("rman mismatch"));
KASSERT(!(rman_get_flags(res) & RF_ACTIVE),
("active resources cannot be adjusted"));
return (rman_adjust_resource(res, start, end));
}
static phandle_t
ofw_pci_get_node(device_t bus, device_t dev)
{
struct ofw_pci_softc *sc;
sc = device_get_softc(bus);
/* We only have one child, the PCI bus, which needs our own node. */
return (sc->sc_node);
}
static int
ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges)
{
int host_address_cells = 1, pci_address_cells = 3, size_cells = 2;
cell_t *base_ranges;
ssize_t nbase_ranges;
int nranges;
int i, j, k;
OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells,
sizeof(host_address_cells));
OF_getencprop(node, "#address-cells", &pci_address_cells,
sizeof(pci_address_cells));
OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells));
nbase_ranges = OF_getproplen(node, "ranges");
if (nbase_ranges <= 0)
return (-1);
nranges = nbase_ranges / sizeof(cell_t) /
(pci_address_cells + host_address_cells + size_cells);
base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
for (i = 0, j = 0; i < nranges; i++) {
ranges[i].pci_hi = base_ranges[j++];
ranges[i].pci = 0;
for (k = 0; k < pci_address_cells - 1; k++) {
ranges[i].pci <<= 32;
ranges[i].pci |= base_ranges[j++];
}
ranges[i].host = 0;
for (k = 0; k < host_address_cells; k++) {
ranges[i].host <<= 32;
ranges[i].host |= base_ranges[j++];
}
ranges[i].size = 0;
for (k = 0; k < size_cells; k++) {
ranges[i].size <<= 32;
ranges[i].size |= base_ranges[j++];
}
}
free(base_ranges, M_DEVBUF);
return (nranges);
}
static struct rman *
ofw_pci_get_rman(struct ofw_pci_softc *sc, int type, u_int flags)
{
switch (type) {
case SYS_RES_IOPORT:
return (&sc->sc_io_rman);
case SYS_RES_MEMORY:
if (sc->sc_have_pmem && (flags & RF_PREFETCHABLE))
return (&sc->sc_pmem_rman);
else
return (&sc->sc_mem_rman);
default:
break;
}
return (NULL);
}

View File

@ -0,0 +1,87 @@
/*-
* Copyright (c) 2011 Nathan Whitehorn
* 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _DEV_OFW_OFWPCI_H_
#define _DEV_OFW_OFWPCI_H_
/*
* Export class definition for inheritance purposes
*/
DECLARE_CLASS(ofw_pci_driver);
struct ofw_pci_cell_info {
pcell_t host_address_cells;
pcell_t pci_address_cell;
pcell_t size_cells;
};
struct ofw_pci_range {
uint32_t pci_hi;
uint64_t pci;
uint64_t host;
uint64_t size;
};
/*
* Quirks for some adapters
*/
enum {
OFW_PCI_QUIRK_RANGES_ON_CHILDREN = 1,
};
struct ofw_pci_softc {
device_t sc_dev;
phandle_t sc_node;
int sc_bus;
int sc_initialized;
int sc_quirks;
int sc_have_pmem;
struct ofw_pci_range *sc_range;
int sc_nrange;
uint64_t sc_range_mask;
struct ofw_pci_cell_info *sc_cell_info;
struct rman sc_io_rman;
struct rman sc_mem_rman;
struct rman sc_pmem_rman;
bus_space_tag_t sc_memt;
bus_dma_tag_t sc_dmat;
int sc_pci_domain;
struct ofw_bus_iinfo sc_pci_iinfo;
};
int ofw_pci_init(device_t);
int ofw_pci_attach(device_t);
int ofw_pci_read_ivar(device_t, device_t, int, uintptr_t *);
int ofw_pci_write_ivar(device_t, device_t, int, uintptr_t);
int ofw_pci_route_interrupt(device_t, device_t, int);
int ofw_pci_nranges(phandle_t, struct ofw_pci_cell_info *);
#endif /* _DEV_OFW_OFWPCI_H_ */

View File

@ -0,0 +1,388 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2011 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Support APIs for Host to PCI bridge drivers and drivers that
* provide PCI domains.
*/
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcib_private.h>
/*
* Try to read the bus number of a host-PCI bridge using appropriate config
* registers.
*/
int
host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func,
uint8_t *busnum)
{
uint32_t id;
id = read_config(bus, slot, func, PCIR_DEVVENDOR, 4);
if (id == 0xffffffff)
return (0);
switch (id) {
case 0x12258086:
/* Intel 824?? */
/* XXX This is a guess */
/* *busnum = read_config(bus, slot, func, 0x41, 1); */
*busnum = bus;
break;
case 0x84c48086:
/* Intel 82454KX/GX (Orion) */
*busnum = read_config(bus, slot, func, 0x4a, 1);
break;
case 0x84ca8086:
/*
* For the 450nx chipset, there is a whole bundle of
* things pretending to be host bridges. The MIOC will
* be seen first and isn't really a pci bridge (the
* actual buses are attached to the PXB's). We need to
* read the registers of the MIOC to figure out the
* bus numbers for the PXB channels.
*
* Since the MIOC doesn't have a pci bus attached, we
* pretend it wasn't there.
*/
return (0);
case 0x84cb8086:
switch (slot) {
case 0x12:
/* Intel 82454NX PXB#0, Bus#A */
*busnum = read_config(bus, 0x10, func, 0xd0, 1);
break;
case 0x13:
/* Intel 82454NX PXB#0, Bus#B */
*busnum = read_config(bus, 0x10, func, 0xd1, 1) + 1;
break;
case 0x14:
/* Intel 82454NX PXB#1, Bus#A */
*busnum = read_config(bus, 0x10, func, 0xd3, 1);
break;
case 0x15:
/* Intel 82454NX PXB#1, Bus#B */
*busnum = read_config(bus, 0x10, func, 0xd4, 1) + 1;
break;
}
break;
/* ServerWorks -- vendor 0x1166 */
case 0x00051166:
case 0x00061166:
case 0x00081166:
case 0x00091166:
case 0x00101166:
case 0x00111166:
case 0x00171166:
case 0x01011166:
case 0x010f1014:
case 0x01101166:
case 0x02011166:
case 0x02251166:
case 0x03021014:
*busnum = read_config(bus, slot, func, 0x44, 1);
break;
/* Compaq/HP -- vendor 0x0e11 */
case 0x60100e11:
*busnum = read_config(bus, slot, func, 0xc8, 1);
break;
default:
/* Don't know how to read bus number. */
return 0;
}
return 1;
}
#ifdef NEW_PCIB
/*
* Return a pointer to a pretty name for a PCI device. If the device
* has a driver attached, the device's name is used, otherwise a name
* is generated from the device's PCI address.
*/
const char *
pcib_child_name(device_t child)
{
static char buf[64];
if (device_get_nameunit(child) != NULL)
return (device_get_nameunit(child));
snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child),
pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
return (buf);
}
/*
* Some Host-PCI bridge drivers know which resource ranges they can
* decode and should only allocate subranges to child PCI devices.
* This API provides a way to manage this. The bridge driver should
* initialize this structure during attach and call
* pcib_host_res_decodes() on each resource range it decodes. It can
* then use pcib_host_res_alloc() and pcib_host_res_adjust() as helper
* routines for BUS_ALLOC_RESOURCE() and BUS_ADJUST_RESOURCE(). This
* API assumes that resources for any decoded ranges can be safely
* allocated from the parent via bus_generic_alloc_resource().
*/
int
pcib_host_res_init(device_t pcib, struct pcib_host_resources *hr)
{
hr->hr_pcib = pcib;
resource_list_init(&hr->hr_rl);
return (0);
}
int
pcib_host_res_free(device_t pcib, struct pcib_host_resources *hr)
{
resource_list_free(&hr->hr_rl);
return (0);
}
int
pcib_host_res_decodes(struct pcib_host_resources *hr, int type, rman_res_t start,
rman_res_t end, u_int flags)
{
struct resource_list_entry *rle;
int rid;
if (bootverbose)
device_printf(hr->hr_pcib, "decoding %d %srange %#jx-%#jx\n",
type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start,
end);
rid = resource_list_add_next(&hr->hr_rl, type, start, end,
end - start + 1);
if (flags & RF_PREFETCHABLE) {
KASSERT(type == SYS_RES_MEMORY,
("only memory is prefetchable"));
rle = resource_list_find(&hr->hr_rl, type, rid);
rle->flags = RLE_PREFETCH;
}
return (0);
}
struct resource *
pcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type,
int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct resource_list_entry *rle;
struct resource *r;
rman_res_t new_start, new_end;
if (flags & RF_PREFETCHABLE)
KASSERT(type == SYS_RES_MEMORY,
("only memory is prefetchable"));
rle = resource_list_find(&hr->hr_rl, type, 0);
if (rle == NULL) {
/*
* No decoding ranges for this resource type, just pass
* the request up to the parent.
*/
return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
start, end, count, flags));
}
restart:
/* Try to allocate from each decoded range. */
for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
if (rle->type != type)
continue;
if (((flags & RF_PREFETCHABLE) != 0) !=
((rle->flags & RLE_PREFETCH) != 0))
continue;
new_start = ummax(start, rle->start);
new_end = ummin(end, rle->end);
if (new_start > new_end ||
new_start + count - 1 > new_end ||
new_start + count < new_start)
continue;
r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid,
new_start, new_end, count, flags);
if (r != NULL) {
if (bootverbose)
device_printf(hr->hr_pcib,
"allocated type %d (%#jx-%#jx) for rid %x of %s\n",
type, rman_get_start(r), rman_get_end(r),
*rid, pcib_child_name(dev));
return (r);
}
}
/*
* If we failed to find a prefetch range for a memory
* resource, try again without prefetch.
*/
if (flags & RF_PREFETCHABLE) {
flags &= ~RF_PREFETCHABLE;
rle = resource_list_find(&hr->hr_rl, type, 0);
goto restart;
}
return (NULL);
}
int
pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type,
struct resource *r, rman_res_t start, rman_res_t end)
{
struct resource_list_entry *rle;
rle = resource_list_find(&hr->hr_rl, type, 0);
if (rle == NULL) {
/*
* No decoding ranges for this resource type, just pass
* the request up to the parent.
*/
return (bus_generic_adjust_resource(hr->hr_pcib, dev, type, r,
start, end));
}
/* Only allow adjustments that stay within a decoded range. */
for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) {
if (rle->start <= start && rle->end >= end)
return (bus_generic_adjust_resource(hr->hr_pcib, dev,
type, r, start, end));
}
return (ERANGE);
}
#ifdef PCI_RES_BUS
struct pci_domain {
int pd_domain;
struct rman pd_bus_rman;
TAILQ_ENTRY(pci_domain) pd_link;
};
static TAILQ_HEAD(, pci_domain) domains = TAILQ_HEAD_INITIALIZER(domains);
/*
* Each PCI domain maintains its own resource manager for PCI bus
* numbers in that domain. Domain objects are created on first use.
* Host to PCI bridge drivers and PCI-PCI bridge drivers should
* allocate their bus ranges from their domain.
*/
static struct pci_domain *
pci_find_domain(int domain)
{
struct pci_domain *d;
char buf[64];
int error;
TAILQ_FOREACH(d, &domains, pd_link) {
if (d->pd_domain == domain)
return (d);
}
snprintf(buf, sizeof(buf), "PCI domain %d bus numbers", domain);
d = malloc(sizeof(*d) + strlen(buf) + 1, M_DEVBUF, M_WAITOK | M_ZERO);
d->pd_domain = domain;
d->pd_bus_rman.rm_start = 0;
d->pd_bus_rman.rm_end = PCI_BUSMAX;
d->pd_bus_rman.rm_type = RMAN_ARRAY;
strcpy((char *)(d + 1), buf);
d->pd_bus_rman.rm_descr = (char *)(d + 1);
error = rman_init(&d->pd_bus_rman);
if (error == 0)
error = rman_manage_region(&d->pd_bus_rman, 0, PCI_BUSMAX);
if (error)
panic("Failed to initialize PCI domain %d rman", domain);
TAILQ_INSERT_TAIL(&domains, d, pd_link);
return (d);
}
struct resource *
pci_domain_alloc_bus(int domain, device_t dev, int *rid, rman_res_t start,
rman_res_t end, rman_res_t count, u_int flags)
{
struct pci_domain *d;
struct resource *res;
if (domain < 0 || domain > PCI_DOMAINMAX)
return (NULL);
d = pci_find_domain(domain);
res = rman_reserve_resource(&d->pd_bus_rman, start, end, count, flags,
dev);
if (res == NULL)
return (NULL);
rman_set_rid(res, *rid);
return (res);
}
int
pci_domain_adjust_bus(int domain, device_t dev, struct resource *r,
rman_res_t start, rman_res_t end)
{
#ifdef INVARIANTS
struct pci_domain *d;
#endif
if (domain < 0 || domain > PCI_DOMAINMAX)
return (EINVAL);
#ifdef INVARIANTS
d = pci_find_domain(domain);
KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
#endif
return (rman_adjust_resource(r, start, end));
}
int
pci_domain_release_bus(int domain, device_t dev, int rid, struct resource *r)
{
#ifdef INVARIANTS
struct pci_domain *d;
#endif
if (domain < 0 || domain > PCI_DOMAINMAX)
return (EINVAL);
#ifdef INVARIANTS
d = pci_find_domain(domain);
KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
#endif
return (rman_release_resource(r));
}
#endif /* PCI_RES_BUS */
#endif /* NEW_PCIB */

View File

@ -0,0 +1,224 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2000 Tsubai Masanari. 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 ``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 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.
*
* $NetBSD: hid.h,v 1.2 2001/08/22 21:05:25 matt Exp $
* $FreeBSD$
*/
#ifndef _POWERPC_HID_H_
#define _POWERPC_HID_H_
/* Hardware Implementation Dependent registers for the PowerPC */
#define HID0_RADIX 0x0080000000000000 /* Enable Radix page tables (POWER9) */
#define HID0_EMCP 0x80000000 /* Enable machine check pin */
#define HID0_DBP 0x40000000 /* Disable 60x bus parity generation */
#define HID0_EBA 0x20000000 /* Enable 60x bus address parity checking */
#define HID0_EBD 0x10000000 /* Enable 60x bus data parity checking */
#define HID0_BCLK 0x08000000 /* CLK_OUT clock type selection */
#define HID0_EICE 0x04000000 /* Enable ICE output */
#define HID0_ECLK 0x02000000 /* CLK_OUT clock type selection */
#define HID0_PAR 0x01000000 /* Disable precharge of ARTRY */
#define HID0_STEN 0x01000000 /* Software table search enable (7450) */
#define HID0_DEEPNAP 0x01000000 /* Enable deep nap mode (970) */
#define HID0_HBATEN 0x00800000 /* High BAT enable (74[45][578]) */
#define HID0_DOZE 0x00800000 /* Enable doze mode */
#define HID0_NAP 0x00400000 /* Enable nap mode */
#define HID0_SLEEP 0x00200000 /* Enable sleep mode */
#define HID0_DPM 0x00100000 /* Enable Dynamic power management */
#define HID0_RISEG 0x00080000 /* Read I-SEG */
#define HID0_TG 0x00040000 /* Timebase Granularity (OEA64) */
#define HID0_BHTCLR 0x00040000 /* Clear branch history table (7450) */
#define HID0_EIEC 0x00040000 /* Enable internal error checking */
#define HID0_XAEN 0x00020000 /* Enable eXtended Addressing (7450) */
#define HID0_NHR 0x00010000 /* Not hard reset */
#define HID0_ICE 0x00008000 /* Enable i-cache */
#define HID0_DCE 0x00004000 /* Enable d-cache */
#define HID0_ILOCK 0x00002000 /* i-cache lock */
#define HID0_DLOCK 0x00001000 /* d-cache lock */
#define HID0_ICFI 0x00000800 /* i-cache flush invalidate */
#define HID0_DCFI 0x00000400 /* d-cache flush invalidate */
#define HID0_SPD 0x00000200 /* Disable speculative cache access */
#define HID0_XBSEN 0x00000100 /* Extended BAT block-size enable (7457) */
#define HID0_IFEM 0x00000100 /* Enable M-bit for I-fetch */
#define HID0_XBSEN 0x00000100 /* Extended BAT block size enable (7455+)*/
#define HID0_SGE 0x00000080 /* Enable store gathering */
#define HID0_DCFA 0x00000040 /* Data cache flush assist */
#define HID0_BTIC 0x00000020 /* Enable BTIC */
#define HID0_LRSTK 0x00000010 /* Link register stack enable (7450) */
#define HID0_ABE 0x00000008 /* Enable address broadcast */
#define HID0_FOLD 0x00000008 /* Branch folding enable (7450) */
#define HID0_BHT 0x00000004 /* Enable branch history table */
#define HID0_NOPTI 0x00000001 /* No-op the dcbt(st) */
#define HID0_AIM_TBEN 0x04000000 /* Time base enable (7450) */
#define HID0_E500_TBEN 0x00004000 /* Time Base and decr. enable */
#define HID0_E500_SEL_TBCLK 0x00002000 /* Select Time Base clock */
#define HID0_E500_MAS7UPDEN 0x00000080 /* Enable MAS7 update (e500v2) */
#define HID0_E500MC_L2MMU_MHD 0x40000000 /* L2MMU Multiple Hit Detection */
#define HID0_BITMASK \
"\20" \
"\040EMCP\037DBP\036EBA\035EBD\034BCLK\033EICE\032ECLK\031PAR" \
"\030DOZE\027NAP\026SLEEP\025DPM\024RISEG\023EIEC\022res\021NHR" \
"\020ICE\017DCE\016ILOCK\015DLOCK\014ICFI\013DCFI\012SPD\011IFEM" \
"\010SGE\007DCFA\006BTIC\005FBIOB\004ABE\003BHT\002NOPDST\001NOPTI"
#define HID0_7450_BITMASK \
"\20" \
"\040EMCP\037b1\036b2\035b3\034b4\033TBEN\032b6\031STEN" \
"\030HBATEN\027NAP\026SLEEP\025DPM\024b12\023BHTCLR\022XAEN\021NHR" \
"\020ICE\017DCE\016ILOCK\015DLOCK\014ICFI\013DCFI\012SPD\011XBSEN" \
"\010SGE\007b25\006BTIC\005LRSTK\004FOLD\003BHT\002NOPDST\001NOPTI"
#define HID0_E500_BITMASK \
"\20" \
"\040EMCP\037b1\036b2\035b3\034b4\033b5\032b6\031b7" \
"\030DOZE\027NAP\026SLEEP\025b11\024b12\023b13\022b14\021b15" \
"\020b16\017TBEN\016SEL_TBCLK\015b19\014b20\013b21\012b22\011b23" \
"\010EN_MAS7_UPDATE\007DCFA\006b26\005b27\004b28\003b29\002b30\001NOPTI"
#define HID0_970_BITMASK \
"\20" \
"\040ONEPPC\037SINGLE\036ISYNCSC\035SERGP\031DEEPNAP\030DOZE" \
"\027NAP\025DPM\023TG\022HANGDETECT\021NHR\020INORDER" \
"\016TBCTRL\015TBEN\012CIABREN\011HDICEEN\001ENATTN"
#define HID0_E500MC_BITMASK \
"\20" \
"\040EMCP\037EN_L2MMU_MHD\036b2\035b3\034b4\033b5\032b6\031b7" \
"\030b8\027b9\026b10\025b11\024b12\023b13\022b14\021b15" \
"\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \
"\010EN_MAS7_UPDATE\007DCFA\006b26\005CIGLSO\004b28\003b29\002b30\001NOPTI"
#define HID0_E5500_BITMASK \
"\20" \
"\040EMCP\037EN_L2MMU_MHD\036b2\035b3\034b4\033b5\032b6\031b7" \
"\030b8\027b9\026b10\025b11\024b12\023b13\022b14\021b15" \
"\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \
"\010b24\007DCFA\006b26\005CIGLSO\004b28\003b29\002b30\001NOPTI"
/*
* HID0 bit definitions per cpu model
*
* bit 603 604 750 7400 7410 7450 7457 e500
* 0 EMCP EMCP EMCP EMCP EMCP - - EMCP
* 1 - ECP DBP - - - - -
* 2 EBA EBA EBA EBA EDA - - -
* 3 EBD EBD EBD EBD EBD - - -
* 4 SBCLK - BCLK BCKL BCLK - - -
* 5 EICE - - - - TBEN TBEN -
* 6 ECLK - ECLK ECLK ECLK - - -
* 7 PAR PAR PAR PAR PAR STEN STEN -
* 8 DOZE - DOZE DOZE DOZE - HBATEN DOZE
* 9 NAP - NAP NAP NAP NAP NAP NAP
* 10 SLEEP - SLEEP SLEEP SLEEP SLEEP SLEEP SLEEP
* 11 DPM - DPM DPM DPM DPM DPM -
* 12 RISEG - - RISEG - - - -
* 13 - - - EIEC EIEC BHTCLR BHTCLR -
* 14 - - - - - XAEN XAEN -
* 15 - NHR NHR NHR NHR NHR NHR -
* 16 ICE ICE ICE ICE ICE ICE ICE -
* 17 DCE DCE DCE DCE DCE DCE DCE TBEN
* 18 ILOCK ILOCK ILOCK ILOCK ILOCK ILOCK ILOCK SEL_TBCLK
* 19 DLOCK DLOCK DLOCK DLOCK DLOCK DLOCK DLOCK -
* 20 ICFI ICFI ICFI ICFI ICFI ICFI ICFI -
* 21 DCFI DCFI DCFI DCFI DCFI DCFI DCFI -
* 22 - - SPD SPD SPG SPD SPD -
* 23 - - IFEM IFTT IFTT - XBSEN -
* 24 - SIE SGE SGE SGE SGE SGE EN_MAS7_UPDATE
* 25 - - DCFA DCFA DCFA - - DCFA
* 26 - - BTIC BTIC BTIC BTIC BTIC -
* 27 FBIOB - - - - LRSTK LRSTK -
* 28 - - ABE - - FOLD FOLD -
* 29 - BHT BHT BHT BHT BHT BHT -
* 30 - - - NOPDST NOPDST NOPDST NOPDST -
* 31 NOOPTI - NOOPTI NOPTI NOPTI NOPTI NOPTI NOPTI
*
* bit e500mc e5500
* 0 EMCP EMCP
* 1 EN_L2MMU_MHD EN_L2MMU_MHD
* 2 - -
* 3 - -
* 4 - -
* 5 - -
* 6 - -
* 7 - -
* 8 - -
* 9 - -
* 10 - -
* 11 - -
* 12 - -
* 13 - -
* 14 - -
* 15 - -
* 16 - -
* 17 - -
* 18 - -
* 19 - -
* 20 - -
* 21 - -
* 22 - -
* 23 - -
* 24 EN_MAS7_UPDATE -
* 25 DCFA DCFA
* 26 - -
* 27 CIGLSO CIGLSO
* 28 - -
* 29 - -
* 30 - -
* 31 NOPTI NOPTI
*
* 604: ECP = Enable cache parity checking
* 604: SIE = Serial instruction execution disable
* 7450: TBEN = Time Base Enable
* 7450: STEN = Software table lookup enable
* 7450: BHTCLR = Branch history clear
* 7450: XAEN = Extended Addressing Enabled
* 7450: LRSTK = Link Register Stack Enable
* 7450: FOLD = Branch folding enable
* 7457: HBATEN = High BAT Enable
* 7457: XBSEN = Extended BAT Block Size Enable
*/
#define HID1_E500_ABE 0x00001000 /* Address broadcast enable */
#define HID1_E500_ASTME 0x00002000 /* Address bus streaming mode enable */
#define HID1_E500_RFXE 0x00020000 /* Read fault exception enable */
#define HID0_E500_DEFAULT_SET (HID0_EMCP | HID0_E500_TBEN | \
HID0_E500_MAS7UPDEN)
#define HID1_E500_DEFAULT_SET (HID1_E500_ABE | HID1_E500_ASTME)
#define HID0_E500MC_DEFAULT_SET (HID0_EMCP | HID0_E500MC_L2MMU_MHD | \
HID0_E500_MAS7UPDEN)
#define HID0_E5500_DEFAULT_SET (HID0_EMCP | HID0_E500MC_L2MMU_MHD)
#define HID5_970_DCBZ_SIZE_HI 0x00000080UL /* dcbz does a 32-byte store */
#define HID4_970_DISABLE_LG_PG 0x00000004ULL /* disables large pages */
#endif /* _POWERPC_HID_H_ */

View File

@ -0,0 +1,306 @@
/*-
* SPDX-License-Identifier: BSD-4-Clause
*
* Copyright (c) 1997 Per Fogelstrom, Opsycon AB and RTMX Inc, USA.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed under OpenBSD by
* Per Fogelstrom Opsycon AB for RTMX Inc, North Carolina, USA.
* 4. 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 ``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 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.
*
* $NetBSD: pio.h,v 1.1 1998/05/15 10:15:54 tsubai Exp $
* $OpenBSD: pio.h,v 1.1 1997/10/13 10:53:47 pefo Exp $
* $FreeBSD$
*/
#ifndef _MACHINE_PIO_H_
#define _MACHINE_PIO_H_
/*
* I/O macros.
*/
/*
* Use sync so that bus space operations cannot sneak out the bottom of
* mutex-protected sections (mutex release does not guarantee completion of
* accesses to caching-inhibited memory on some systems)
*/
#define powerpc_iomb() __asm __volatile("sync" : : : "memory")
static __inline void
__outb(volatile u_int8_t *a, u_int8_t v)
{
*a = v;
powerpc_iomb();
}
static __inline void
__outw(volatile u_int16_t *a, u_int16_t v)
{
*a = v;
powerpc_iomb();
}
static __inline void
__outl(volatile u_int32_t *a, u_int32_t v)
{
*a = v;
powerpc_iomb();
}
static __inline void
__outll(volatile u_int64_t *a, u_int64_t v)
{
*a = v;
powerpc_iomb();
}
static __inline void
__outwrb(volatile u_int16_t *a, u_int16_t v)
{
__asm__ volatile("sthbrx %0, 0, %1" :: "r"(v), "r"(a));
powerpc_iomb();
}
static __inline void
__outlrb(volatile u_int32_t *a, u_int32_t v)
{
__asm__ volatile("stwbrx %0, 0, %1" :: "r"(v), "r"(a));
powerpc_iomb();
}
static __inline u_int8_t
__inb(volatile u_int8_t *a)
{
u_int8_t _v_;
_v_ = *a;
powerpc_iomb();
return _v_;
}
static __inline u_int16_t
__inw(volatile u_int16_t *a)
{
u_int16_t _v_;
_v_ = *a;
powerpc_iomb();
return _v_;
}
static __inline u_int32_t
__inl(volatile u_int32_t *a)
{
u_int32_t _v_;
_v_ = *a;
powerpc_iomb();
return _v_;
}
static __inline u_int64_t
__inll(volatile u_int64_t *a)
{
u_int64_t _v_;
_v_ = *a;
powerpc_iomb();
return _v_;
}
static __inline u_int16_t
__inwrb(volatile u_int16_t *a)
{
u_int16_t _v_;
__asm__ volatile("lhbrx %0, 0, %1" : "=r"(_v_) : "r"(a));
powerpc_iomb();
return _v_;
}
static __inline u_int32_t
__inlrb(volatile u_int32_t *a)
{
u_int32_t _v_;
__asm__ volatile("lwbrx %0, 0, %1" : "=r"(_v_) : "r"(a));
powerpc_iomb();
return _v_;
}
#define outb(a,v) (__outb((volatile u_int8_t *)(a), v))
#define out8(a,v) outb(a,v)
#define outw(a,v) (__outw((volatile u_int16_t *)(a), v))
#define out16(a,v) outw(a,v)
#define outl(a,v) (__outl((volatile u_int32_t *)(a), v))
#define out32(a,v) outl(a,v)
#define outll(a,v) (__outll((volatile u_int64_t *)(a), v))
#define out64(a,v) outll(a,v)
#define inb(a) (__inb((volatile u_int8_t *)(a)))
#define in8(a) inb(a)
#define inw(a) (__inw((volatile u_int16_t *)(a)))
#define in16(a) inw(a)
#define inl(a) (__inl((volatile u_int32_t *)(a)))
#define in32(a) inl(a)
#define inll(a) (__inll((volatile u_int64_t *)(a)))
#define in64(a) inll(a)
#define out8rb(a,v) outb(a,v)
#define outwrb(a,v) (__outwrb((volatile u_int16_t *)(a), v))
#define out16rb(a,v) outwrb(a,v)
#define outlrb(a,v) (__outlrb((volatile u_int32_t *)(a), v))
#define out32rb(a,v) outlrb(a,v)
#define in8rb(a) inb(a)
#define inwrb(a) (__inwrb((volatile u_int16_t *)(a)))
#define in16rb(a) inwrb(a)
#define inlrb(a) (__inlrb((volatile u_int32_t *)(a)))
#define in32rb(a) inlrb(a)
static __inline void
__outsb(volatile u_int8_t *a, const u_int8_t *s, size_t c)
{
while (c--)
*a = *s++;
powerpc_iomb();
}
static __inline void
__outsw(volatile u_int16_t *a, const u_int16_t *s, size_t c)
{
while (c--)
*a = *s++;
powerpc_iomb();
}
static __inline void
__outsl(volatile u_int32_t *a, const u_int32_t *s, size_t c)
{
while (c--)
*a = *s++;
powerpc_iomb();
}
static __inline void
__outsll(volatile u_int64_t *a, const u_int64_t *s, size_t c)
{
while (c--)
*a = *s++;
powerpc_iomb();
}
static __inline void
__outswrb(volatile u_int16_t *a, const u_int16_t *s, size_t c)
{
while (c--)
__asm__ volatile("sthbrx %0, 0, %1" :: "r"(*s++), "r"(a));
powerpc_iomb();
}
static __inline void
__outslrb(volatile u_int32_t *a, const u_int32_t *s, size_t c)
{
while (c--)
__asm__ volatile("stwbrx %0, 0, %1" :: "r"(*s++), "r"(a));
powerpc_iomb();
}
static __inline void
__insb(volatile u_int8_t *a, u_int8_t *d, size_t c)
{
while (c--)
*d++ = *a;
powerpc_iomb();
}
static __inline void
__insw(volatile u_int16_t *a, u_int16_t *d, size_t c)
{
while (c--)
*d++ = *a;
powerpc_iomb();
}
static __inline void
__insl(volatile u_int32_t *a, u_int32_t *d, size_t c)
{
while (c--)
*d++ = *a;
powerpc_iomb();
}
static __inline void
__insll(volatile u_int64_t *a, u_int64_t *d, size_t c)
{
while (c--)
*d++ = *a;
powerpc_iomb();
}
static __inline void
__inswrb(volatile u_int16_t *a, u_int16_t *d, size_t c)
{
while (c--)
__asm__ volatile("lhbrx %0, 0, %1" : "=r"(*d++) : "r"(a));
powerpc_iomb();
}
static __inline void
__inslrb(volatile u_int32_t *a, u_int32_t *d, size_t c)
{
while (c--)
__asm__ volatile("lwbrx %0, 0, %1" : "=r"(*d++) : "r"(a));
powerpc_iomb();
}
#define outsb(a,s,c) (__outsb((volatile u_int8_t *)(a), s, c))
#define outs8(a,s,c) outsb(a,s,c)
#define outsw(a,s,c) (__outsw((volatile u_int16_t *)(a), s, c))
#define outs16(a,s,c) outsw(a,s,c)
#define outsl(a,s,c) (__outsl((volatile u_int32_t *)(a), s, c))
#define outs32(a,s,c) outsl(a,s,c)
#define outsll(a,s,c) (__outsll((volatile u_int64_t *)(a), s, c))
#define outs64(a,s,c) outsll(a,s,c)
#define insb(a,d,c) (__insb((volatile u_int8_t *)(a), d, c))
#define ins8(a,d,c) insb(a,d,c)
#define insw(a,d,c) (__insw((volatile u_int16_t *)(a), d, c))
#define ins16(a,d,c) insw(a,d,c)
#define insl(a,d,c) (__insl((volatile u_int32_t *)(a), d, c))
#define ins32(a,d,c) insl(a,d,c)
#define insll(a,d,c) (__insll((volatile u_int64_t *)(a), d, c))
#define ins64(a,d,c) insll(a,d,c)
#define outs8rb(a,s,c) outsb(a,s,c)
#define outswrb(a,s,c) (__outswrb((volatile u_int16_t *)(a), s, c))
#define outs16rb(a,s,c) outswrb(a,s,c)
#define outslrb(a,s,c) (__outslrb((volatile u_int32_t *)(a), s, c))
#define outs32rb(a,s,c) outslrb(a,s,c)
#define ins8rb(a,d,c) insb(a,d,c)
#define inswrb(a,d,c) (__inswrb((volatile u_int16_t *)(a), d, c))
#define ins16rb(a,d,c) inswrb(a,d,c)
#define inslrb(a,d,c) (__inslrb((volatile u_int32_t *)(a), d, c))
#define ins32rb(a,d,c) inslrb(a,d,c)
#endif /*_MACHINE_PIO_H_*/

View File

@ -0,0 +1,91 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2005 Peter Grehan
* 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.
*
* 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.
*
* $FreeBSD$
*/
#ifndef _MACHINE_PLATFORMVAR_H_
#define _MACHINE_PLATFORMVAR_H_
/*
* A PowerPC platform implementation is declared with a kernel object and
* an associated method table, similar to a device driver.
*
* e.g.
*
* static platform_method_t chrp_methods[] = {
* PLATFORMMETHOD(platform_probe, chrp_probe),
* PLATFORMMETHOD(platform_mem_regions, ofw_mem_regions),
* ...
* PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
* { 0, 0 }
* };
*
* static platform_def_t chrp_platform = {
* "chrp",
* chrp_methods,
* sizeof(chrp_platform_softc), // or 0 if no softc
* };
*
* PLATFORM_DEF(chrp_platform);
*/
#include <sys/kobj.h>
struct platform_kobj {
/*
* A platform instance is a kernel object
*/
KOBJ_FIELDS;
/*
* Utility elements that an instance may use
*/
struct mtx platform_mtx; /* available for instance use */
void *platform_iptr; /* instance data pointer */
/*
* Opaque data that can be overlaid with an instance-private
* structure. Platform code can test that this is large enough at
* compile time with a sizeof() test againt it's softc. There
* is also a run-time test when the platform kernel object is
* registered.
*/
#define PLATFORM_OPAQUESZ 64
u_int platform_opaque[PLATFORM_OPAQUESZ];
};
typedef struct platform_kobj *platform_t;
typedef struct kobj_class platform_def_t;
#define platform_method_t kobj_method_t
#define PLATFORMMETHOD KOBJMETHOD
#define PLATFORMMETHOD_END KOBJMETHOD_END
#define PLATFORM_DEF(name) DATA_SET(platform_set, name)
#endif /* _MACHINE_PLATFORMVAR_H_ */

View File

@ -0,0 +1,361 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2008 Semihalf, Rafal Jaworowski
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <rtems/bsd/local/opt_platform.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/reboot.h>
#include <sys/rman.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#ifndef __rtems__
#include <machine/machdep.h>
#endif /* __rtems__ */
#include <machine/pio.h>
#include <machine/spr.h>
#include <dev/fdt/fdt_common.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
#include <powerpc/mpc85xx/mpc85xx.h>
/*
* MPC85xx system specific routines
*/
uint32_t
ccsr_read4(uintptr_t addr)
{
volatile uint32_t *ptr = (void *)addr;
return (*ptr);
}
void
ccsr_write4(uintptr_t addr, uint32_t val)
{
volatile uint32_t *ptr = (void *)addr;
*ptr = val;
powerpc_iomb();
}
int
law_getmax(void)
{
uint32_t ver;
int law_max;
ver = SVR_VER(mfspr(SPR_SVR));
switch (ver) {
case SVR_MPC8555:
case SVR_MPC8555E:
law_max = 8;
break;
case SVR_MPC8533:
case SVR_MPC8533E:
case SVR_MPC8548:
case SVR_MPC8548E:
law_max = 10;
break;
case SVR_P5020:
case SVR_P5020E:
case SVR_P5021:
case SVR_P5021E:
case SVR_P5040:
case SVR_P5040E:
law_max = 32;
break;
default:
law_max = 8;
}
return (law_max);
}
static inline void
law_write(uint32_t n, uint64_t bar, uint32_t sr)
{
if (mpc85xx_is_qoriq()) {
ccsr_write4(OCP85XX_LAWBARH(n), bar >> 32);
ccsr_write4(OCP85XX_LAWBARL(n), bar);
ccsr_write4(OCP85XX_LAWSR_QORIQ(n), sr);
ccsr_read4(OCP85XX_LAWSR_QORIQ(n));
} else {
ccsr_write4(OCP85XX_LAWBAR(n), bar >> 12);
ccsr_write4(OCP85XX_LAWSR_85XX(n), sr);
ccsr_read4(OCP85XX_LAWSR_85XX(n));
}
/*
* The last write to LAWAR should be followed by a read
* of LAWAR before any device try to use any of windows.
* What more the read of LAWAR should be followed by isync
* instruction.
*/
isync();
}
static inline void
law_read(uint32_t n, uint64_t *bar, uint32_t *sr)
{
if (mpc85xx_is_qoriq()) {
*bar = (uint64_t)ccsr_read4(OCP85XX_LAWBARH(n)) << 32 |
ccsr_read4(OCP85XX_LAWBARL(n));
*sr = ccsr_read4(OCP85XX_LAWSR_QORIQ(n));
} else {
*bar = (uint64_t)ccsr_read4(OCP85XX_LAWBAR(n)) << 12;
*sr = ccsr_read4(OCP85XX_LAWSR_85XX(n));
}
}
static int
law_find_free(void)
{
uint32_t i,sr;
uint64_t bar;
int law_max;
law_max = law_getmax();
/* Find free LAW */
for (i = 0; i < law_max; i++) {
law_read(i, &bar, &sr);
if ((sr & 0x80000000) == 0)
break;
}
return (i);
}
#define _LAW_SR(trgt,size) (0x80000000 | (trgt << 20) | \
(flsl(size + (size - 1)) - 2))
int
law_enable(int trgt, uint64_t bar, uint32_t size)
{
uint64_t bar_tmp;
uint32_t sr, sr_tmp;
int i, law_max;
if (size == 0)
return (0);
law_max = law_getmax();
sr = _LAW_SR(trgt, size);
/* Bail if already programmed. */
for (i = 0; i < law_max; i++) {
law_read(i, &bar_tmp, &sr_tmp);
if (sr == sr_tmp && bar == bar_tmp)
return (0);
}
/* Find an unused access window. */
i = law_find_free();
if (i == law_max)
return (ENOSPC);
law_write(i, bar, sr);
return (0);
}
int
law_disable(int trgt, uint64_t bar, uint32_t size)
{
uint64_t bar_tmp;
uint32_t sr, sr_tmp;
int i, law_max;
law_max = law_getmax();
sr = _LAW_SR(trgt, size);
/* Find and disable requested LAW. */
for (i = 0; i < law_max; i++) {
law_read(i, &bar_tmp, &sr_tmp);
if (sr == sr_tmp && bar == bar_tmp) {
law_write(i, 0, 0);
return (0);
}
}
return (ENOENT);
}
int
law_pci_target(struct resource *res, int *trgt_mem, int *trgt_io)
{
u_long start;
uint32_t ver;
int trgt, rv;
ver = SVR_VER(mfspr(SPR_SVR));
start = rman_get_start(res) & 0xf000;
rv = 0;
trgt = -1;
switch (start) {
case 0x0000:
case 0x8000:
trgt = 0;
break;
case 0x1000:
case 0x9000:
trgt = 1;
break;
case 0x2000:
case 0xa000:
if (ver == SVR_MPC8548E || ver == SVR_MPC8548)
trgt = 3;
else
trgt = 2;
break;
case 0x3000:
case 0xb000:
if (ver == SVR_MPC8548E || ver == SVR_MPC8548)
rv = EINVAL;
else
trgt = 3;
break;
default:
rv = ENXIO;
}
if (rv == 0) {
*trgt_mem = trgt;
*trgt_io = trgt;
}
return (rv);
}
static void
l3cache_inval(void)
{
/* Flash invalidate the CPC and clear all the locks */
ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_FI |
OCP85XX_CPC_CSR0_LFC);
while (ccsr_read4(OCP85XX_CPC_CSR0) & (OCP85XX_CPC_CSR0_FI |
OCP85XX_CPC_CSR0_LFC))
;
}
static void
l3cache_enable(void)
{
ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_CE |
OCP85XX_CPC_CSR0_PE);
/* Read back to sync write */
ccsr_read4(OCP85XX_CPC_CSR0);
}
void
mpc85xx_enable_l3_cache(void)
{
uint32_t csr, size, ver;
/* Enable L3 CoreNet Platform Cache (CPC) */
ver = SVR_VER(mfspr(SPR_SVR));
if (ver == SVR_P2041 || ver == SVR_P2041E || ver == SVR_P3041 ||
ver == SVR_P3041E || ver == SVR_P5020 || ver == SVR_P5020E) {
csr = ccsr_read4(OCP85XX_CPC_CSR0);
if ((csr & OCP85XX_CPC_CSR0_CE) == 0) {
l3cache_inval();
l3cache_enable();
}
csr = ccsr_read4(OCP85XX_CPC_CSR0);
if ((boothowto & RB_VERBOSE) != 0 ||
(csr & OCP85XX_CPC_CSR0_CE) == 0) {
size = OCP85XX_CPC_CFG0_SZ_K(ccsr_read4(OCP85XX_CPC_CFG0));
printf("L3 Corenet Platform Cache: %d KB %sabled\n",
size, (csr & OCP85XX_CPC_CSR0_CE) == 0 ?
"dis" : "en");
}
}
}
int
mpc85xx_is_qoriq(void)
{
uint16_t pvr = mfpvr() >> 16;
/* QorIQ register set is only in e500mc and derivative core based SoCs. */
if (pvr == FSL_E500mc || pvr == FSL_E5500 || pvr == FSL_E6500)
return (1);
return (0);
}
uint32_t
mpc85xx_get_platform_clock(void)
{
phandle_t soc;
static uint32_t freq;
if (freq != 0)
return (freq);
soc = OF_finddevice("/soc");
/* freq isn't modified on error. */
OF_getencprop(soc, "bus-frequency", (void *)&freq, sizeof(freq));
return (freq);
}
uint32_t
mpc85xx_get_system_clock(void)
{
uint32_t freq;
freq = mpc85xx_get_platform_clock();
return (freq / 2);
}

View File

@ -0,0 +1,177 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2008 Semihalf, Rafal Jaworowski
* Copyright 2006 by Juniper Networks.
* 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.
*
* 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 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.
*
* $FreeBSD$
*/
#ifndef _MPC85XX_H_
#define _MPC85XX_H_
#include <machine/platformvar.h>
/*
* Configuration control and status registers
*/
extern vm_offset_t ccsrbar_va;
extern vm_paddr_t ccsrbar_pa;
extern vm_size_t ccsrbar_size;
#define CCSRBAR_VA ccsrbar_va
#define OCP85XX_CCSRBAR (CCSRBAR_VA + 0x0)
#define OCP85XX_BPTR (CCSRBAR_VA + 0x20)
#define OCP85XX_BSTRH (CCSRBAR_VA + 0x20)
#define OCP85XX_BSTRL (CCSRBAR_VA + 0x24)
#define OCP85XX_BSTAR (CCSRBAR_VA + 0x28)
#define OCP85XX_COREDISR (CCSRBAR_VA + 0xE0094)
#define OCP85XX_BRR (CCSRBAR_VA + 0xE00E4)
/*
* Run Control and Power Management registers
*/
#define CCSR_CTBENR (CCSRBAR_VA + 0xE2084)
#define CCSR_CTBCKSELR (CCSRBAR_VA + 0xE208C)
#define CCSR_CTBCHLTCR (CCSRBAR_VA + 0xE2094)
/*
* DDR Memory controller.
*/
#define OCP85XX_DDR1_CS0_CONFIG (CCSRBAR_VA + 0x8080)
/*
* E500 Coherency Module registers
*/
#define OCP85XX_EEBPCR (CCSRBAR_VA + 0x1010)
/*
* Local access registers
*/
/* Write order: OCP_LAWBARH -> OCP_LAWBARL -> OCP_LAWSR */
#define OCP85XX_LAWBARH(n) (CCSRBAR_VA + 0xc00 + 0x10 * (n))
#define OCP85XX_LAWBARL(n) (CCSRBAR_VA + 0xc04 + 0x10 * (n))
#define OCP85XX_LAWSR_QORIQ(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n))
#define OCP85XX_LAWBAR(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n))
#define OCP85XX_LAWSR_85XX(n) (CCSRBAR_VA + 0xc10 + 0x10 * (n))
#define OCP85XX_LAWSR(n) (mpc85xx_is_qoriq() ? OCP85XX_LAWSR_QORIQ(n) : \
OCP85XX_LAWSR_85XX(n))
/* Attribute register */
#define OCP85XX_ENA_MASK 0x80000000
#define OCP85XX_DIS_MASK 0x7fffffff
#define OCP85XX_TGTIF_LBC_QORIQ 0x1f
#define OCP85XX_TGTIF_RAM_INTL_QORIQ 0x14
#define OCP85XX_TGTIF_RAM1_QORIQ 0x10
#define OCP85XX_TGTIF_RAM2_QORIQ 0x11
#define OCP85XX_TGTIF_BMAN 0x18
#define OCP85XX_TGTIF_DCSR 0x1D
#define OCP85XX_TGTIF_QMAN 0x3C
#define OCP85XX_TRGT_SHIFT_QORIQ 20
#define OCP85XX_TGTIF_LBC_85XX 0x04
#define OCP85XX_TGTIF_RAM_INTL_85XX 0x0b
#define OCP85XX_TGTIF_RIO_85XX 0x0c
#define OCP85XX_TGTIF_RAM1_85XX 0x0f
#define OCP85XX_TGTIF_RAM2_85XX 0x16
#define OCP85XX_TGTIF_LBC \
(mpc85xx_is_qoriq() ? OCP85XX_TGTIF_LBC_QORIQ : OCP85XX_TGTIF_LBC_85XX)
#define OCP85XX_TGTIF_RAM_INTL \
(mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM_INTL_QORIQ : OCP85XX_TGTIF_RAM_INTL_85XX)
#define OCP85XX_TGTIF_RIO \
(mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RIO_QORIQ : OCP85XX_TGTIF_RIO_85XX)
#define OCP85XX_TGTIF_RAM1 \
(mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM1_QORIQ : OCP85XX_TGTIF_RAM1_85XX)
#define OCP85XX_TGTIF_RAM2 \
(mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM2_QORIQ : OCP85XX_TGTIF_RAM2_85XX)
/*
* L2 cache registers
*/
#define OCP85XX_L2CTL (CCSRBAR_VA + 0x20000)
/*
* L3 CoreNet platform cache (CPC) registers
*/
#define OCP85XX_CPC_CSR0 (CCSRBAR_VA + 0x10000)
#define OCP85XX_CPC_CSR0_CE 0x80000000
#define OCP85XX_CPC_CSR0_PE 0x40000000
#define OCP85XX_CPC_CSR0_FI 0x00200000
#define OCP85XX_CPC_CSR0_WT 0x00080000
#define OCP85XX_CPC_CSR0_FL 0x00000800
#define OCP85XX_CPC_CSR0_LFC 0x00000400
#define OCP85XX_CPC_CFG0 (CCSRBAR_VA + 0x10008)
#define OCP85XX_CPC_CFG_SZ_MASK 0x00003fff
#define OCP85XX_CPC_CFG0_SZ_K(x) (((x) & OCP85XX_CPC_CFG_SZ_MASK) << 6)
/*
* Power-On Reset configuration
*/
#define OCP85XX_PORDEVSR (CCSRBAR_VA + 0xe000c)
#define OCP85XX_PORDEVSR_IO_SEL 0x00780000
#define OCP85XX_PORDEVSR_IO_SEL_SHIFT 19
#define OCP85XX_PORDEVSR2 (CCSRBAR_VA + 0xe0014)
/*
* Status Registers.
*/
#define OCP85XX_RSTCR (CCSRBAR_VA + 0xe00b0)
#define OCP85XX_CLKDVDR (CCSRBAR_VA + 0xe0800)
#define OCP85XX_CLKDVDR_PXCKEN 0x80000000
#define OCP85XX_CLKDVDR_SSICKEN 0x20000000
#define OCP85XX_CLKDVDR_PXCKINV 0x10000000
#define OCP85XX_CLKDVDR_PXCLK_MASK 0x00FF0000
#define OCP85XX_CLKDVDR_SSICLK_MASK 0x000000FF
/*
* Run Control/Power Management Registers.
*/
#define OCP85XX_RCPM_CDOZSR (CCSRBAR_VA + 0xe2004)
#define OCP85XX_RCPM_CDOZCR (CCSRBAR_VA + 0xe200c)
/*
* Prototypes.
*/
uint32_t ccsr_read4(uintptr_t addr);
void ccsr_write4(uintptr_t addr, uint32_t val);
int law_enable(int trgt, uint64_t bar, uint32_t size);
int law_disable(int trgt, uint64_t bar, uint32_t size);
int law_getmax(void);
int law_pci_target(struct resource *, int *, int *);
DECLARE_CLASS(mpc85xx_platform);
int mpc85xx_attach(platform_t);
void mpc85xx_enable_l3_cache(void);
int mpc85xx_is_qoriq(void);
uint32_t mpc85xx_get_platform_clock(void);
uint32_t mpc85xx_get_system_clock(void);
#endif /* _MPC85XX_H_ */

View File

@ -0,0 +1,959 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright 2006-2007 by Juniper Networks.
* Copyright 2008 Semihalf.
* Copyright 2010 The FreeBSD Foundation
* All rights reserved.
*
* 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. 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 ``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 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/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ktr.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <sys/rman.h>
#include <sys/endian.h>
#include <sys/vmem.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <dev/ofw/ofw_pci.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/ofwpci.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcib_private.h>
#include <rtems/bsd/local/ofw_bus_if.h>
#include <rtems/bsd/local/pcib_if.h>
#include <rtems/bsd/local/pic_if.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <machine/intr_machdep.h>
#include <powerpc/mpc85xx/mpc85xx.h>
#define REG_CFG_ADDR 0x0000
#define CONFIG_ACCESS_ENABLE 0x80000000
#define REG_CFG_DATA 0x0004
#define REG_INT_ACK 0x0008
#define REG_PEX_IP_BLK_REV1 0x0bf8
#define IP_MJ_M 0x0000ff00
#define IP_MJ_S 8
#define IP_MN_M 0x000000ff
#define IP_MN_S 0
#define REG_POTAR(n) (0x0c00 + 0x20 * (n))
#define REG_POTEAR(n) (0x0c04 + 0x20 * (n))
#define REG_POWBAR(n) (0x0c08 + 0x20 * (n))
#define REG_POWAR(n) (0x0c10 + 0x20 * (n))
#define REG_PITAR(n) (0x0e00 - 0x20 * (n))
#define REG_PIWBAR(n) (0x0e08 - 0x20 * (n))
#define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n))
#define REG_PIWAR(n) (0x0e10 - 0x20 * (n))
#define PIWAR_EN 0x80000000
#define PIWAR_PF 0x40000000
#define PIWAR_TRGT_M 0x00f00000
#define PIWAR_TRGT_S 20
#define PIWAR_TRGT_CCSR 0xe
#define PIWAR_TRGT_LOCAL 0xf
#define REG_PEX_MES_DR 0x0020
#define REG_PEX_MES_IER 0x0028
#define REG_PEX_ERR_DR 0x0e00
#define REG_PEX_ERR_EN 0x0e08
#define REG_PEX_ERR_DR 0x0e00
#define REG_PEX_ERR_DR_ME 0x80000000
#define REG_PEX_ERR_DR_PCT 0x800000
#define REG_PEX_ERR_DR_PAT 0x400000
#define REG_PEX_ERR_DR_PCAC 0x200000
#define REG_PEX_ERR_DR_PNM 0x100000
#define REG_PEX_ERR_DR_CDNSC 0x80000
#define REG_PEX_ERR_DR_CRSNC 0x40000
#define REG_PEX_ERR_DR_ICCA 0x20000
#define REG_PEX_ERR_DR_IACA 0x10000
#define REG_PEX_ERR_DR_CRST 0x8000
#define REG_PEX_ERR_DR_MIS 0x4000
#define REG_PEX_ERR_DR_IOIS 0x2000
#define REG_PEX_ERR_DR_CIS 0x1000
#define REG_PEX_ERR_DR_CIEP 0x800
#define REG_PEX_ERR_DR_IOIEP 0x400
#define REG_PEX_ERR_DR_OAC 0x200
#define REG_PEX_ERR_DR_IOIA 0x100
#define REG_PEX_ERR_DR_IMBA 0x80
#define REG_PEX_ERR_DR_IIOBA 0x40
#define REG_PEX_ERR_DR_LDDE 0x20
#define REG_PEX_ERR_EN 0x0e08
#define PCIR_LTSSM 0x404
#define LTSSM_STAT_L0 0x16
#define DEVFN(b, s, f) ((b << 16) | (s << 8) | f)
#define FSL_NUM_MSIS 256 /* 8 registers of 32 bits (8 hardware IRQs) */
struct fsl_pcib_softc {
struct ofw_pci_softc pci_sc;
device_t sc_dev;
struct mtx sc_cfg_mtx;
int sc_ip_maj;
int sc_ip_min;
int sc_iomem_target;
bus_addr_t sc_iomem_start, sc_iomem_end;
int sc_ioport_target;
bus_addr_t sc_ioport_start, sc_ioport_end;
struct resource *sc_res;
bus_space_handle_t sc_bsh;
bus_space_tag_t sc_bst;
int sc_rid;
struct resource *sc_irq_res;
void *sc_ih;
int sc_busnr;
int sc_pcie;
uint8_t sc_pcie_capreg; /* PCI-E Capability Reg Set */
};
struct fsl_pcib_err_dr {
const char *msg;
uint32_t err_dr_mask;
};
struct fsl_msi_map {
SLIST_ENTRY(fsl_msi_map) slist;
uint32_t irq_base;
bus_addr_t target;
};
SLIST_HEAD(msi_head, fsl_msi_map) fsl_msis = SLIST_HEAD_INITIALIZER(msi_head);
static const struct fsl_pcib_err_dr pci_err[] = {
{"ME", REG_PEX_ERR_DR_ME},
{"PCT", REG_PEX_ERR_DR_PCT},
{"PAT", REG_PEX_ERR_DR_PAT},
{"PCAC", REG_PEX_ERR_DR_PCAC},
{"PNM", REG_PEX_ERR_DR_PNM},
{"CDNSC", REG_PEX_ERR_DR_CDNSC},
{"CRSNC", REG_PEX_ERR_DR_CRSNC},
{"ICCA", REG_PEX_ERR_DR_ICCA},
{"IACA", REG_PEX_ERR_DR_IACA},
{"CRST", REG_PEX_ERR_DR_CRST},
{"MIS", REG_PEX_ERR_DR_MIS},
{"IOIS", REG_PEX_ERR_DR_IOIS},
{"CIS", REG_PEX_ERR_DR_CIS},
{"CIEP", REG_PEX_ERR_DR_CIEP},
{"IOIEP", REG_PEX_ERR_DR_IOIEP},
{"OAC", REG_PEX_ERR_DR_OAC},
{"IOIA", REG_PEX_ERR_DR_IOIA},
{"IMBA", REG_PEX_ERR_DR_IMBA},
{"IIOBA", REG_PEX_ERR_DR_IIOBA},
{"LDDE", REG_PEX_ERR_DR_LDDE}
};
/* Local forward declerations. */
static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int,
u_int, int);
static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int,
u_int, uint32_t, int);
static int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *);
static void fsl_pcib_err_init(device_t);
static void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, uint64_t,
uint64_t, uint64_t);
static void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, uint64_t,
uint64_t, uint64_t);
/* Forward declerations. */
static int fsl_pcib_attach(device_t);
static int fsl_pcib_detach(device_t);
static int fsl_pcib_probe(device_t);
static int fsl_pcib_maxslots(device_t);
static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int);
static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
uint32_t, int);
static int fsl_pcib_alloc_msi(device_t dev, device_t child,
int count, int maxcount, int *irqs);
static int fsl_pcib_release_msi(device_t dev, device_t child,
int count, int *irqs);
static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq);
static int fsl_pcib_release_msix(device_t dev, device_t child, int irq);
static int fsl_pcib_map_msi(device_t dev, device_t child,
int irq, uint64_t *addr, uint32_t *data);
static vmem_t *msi_vmem; /* Global MSI vmem, holds all MSI ranges. */
/*
* Bus interface definitions.
*/
static device_method_t fsl_pcib_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, fsl_pcib_probe),
DEVMETHOD(device_attach, fsl_pcib_attach),
DEVMETHOD(device_detach, fsl_pcib_detach),
/* pcib interface */
DEVMETHOD(pcib_maxslots, fsl_pcib_maxslots),
DEVMETHOD(pcib_read_config, fsl_pcib_read_config),
DEVMETHOD(pcib_write_config, fsl_pcib_write_config),
DEVMETHOD(pcib_alloc_msi, fsl_pcib_alloc_msi),
DEVMETHOD(pcib_release_msi, fsl_pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, fsl_pcib_alloc_msix),
DEVMETHOD(pcib_release_msix, fsl_pcib_release_msix),
DEVMETHOD(pcib_map_msi, fsl_pcib_map_msi),
DEVMETHOD_END
};
static devclass_t fsl_pcib_devclass;
DEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods,
sizeof(struct fsl_pcib_softc), ofw_pci_driver);
EARLY_DRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0,
BUS_PASS_BUS);
static void
fsl_pcib_err_intr(void *v)
{
struct fsl_pcib_softc *sc;
device_t dev;
uint32_t err_reg, clear_reg;
uint8_t i;
dev = (device_t)v;
sc = device_get_softc(dev);
clear_reg = 0;
err_reg = bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR);
/* Check which one error occurred */
for (i = 0; i < sizeof(pci_err)/sizeof(struct fsl_pcib_err_dr); i++) {
if (err_reg & pci_err[i].err_dr_mask) {
device_printf(dev, "PCI %d: report %s error\n",
device_get_unit(dev), pci_err[i].msg);
clear_reg |= pci_err[i].err_dr_mask;
}
}
/* Clear pending errors */
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, clear_reg);
}
static int
fsl_pcib_probe(device_t dev)
{
if (ofw_bus_get_type(dev) == NULL ||
strcmp(ofw_bus_get_type(dev), "pci") != 0)
return (ENXIO);
if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") ||
ofw_bus_is_compatible(dev, "fsl,mpc8540-pcie") ||
ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie") ||
ofw_bus_is_compatible(dev, "fsl,p5020-pcie") ||
ofw_bus_is_compatible(dev, "fsl,qoriq-pcie-v2.2") ||
ofw_bus_is_compatible(dev, "fsl,qoriq-pcie")))
return (ENXIO);
device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller");
return (BUS_PROBE_DEFAULT);
}
static int
fsl_pcib_attach(device_t dev)
{
struct fsl_pcib_softc *sc;
phandle_t node;
uint32_t cfgreg, brctl, ipreg;
int error, rid;
uint8_t ltssm, capptr;
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->sc_rid = 0;
sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
RF_ACTIVE);
if (sc->sc_res == NULL) {
device_printf(dev, "could not map I/O memory\n");
return (ENXIO);
}
sc->sc_bst = rman_get_bustag(sc->sc_res);
sc->sc_bsh = rman_get_bushandle(sc->sc_res);
sc->sc_busnr = 0;
ipreg = bus_read_4(sc->sc_res, REG_PEX_IP_BLK_REV1);
sc->sc_ip_min = (ipreg & IP_MN_M) >> IP_MN_S;
sc->sc_ip_maj = (ipreg & IP_MJ_M) >> IP_MJ_S;
mtx_init(&sc->sc_cfg_mtx, "pcicfg", NULL, MTX_SPIN);
cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2);
if (cfgreg != 0x1057 && cfgreg != 0x1957)
goto err;
capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1);
while (capptr != 0) {
cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2);
switch (cfgreg & 0xff) {
case PCIY_PCIX:
break;
case PCIY_EXPRESS:
sc->sc_pcie = 1;
sc->sc_pcie_capreg = capptr;
break;
}
capptr = (cfgreg >> 8) & 0xff;
}
node = ofw_bus_get_node(dev);
/*
* Initialize generic OF PCI interface (ranges, etc.)
*/
error = ofw_pci_init(dev);
if (error)
return (error);
/*
* Configure decode windows for PCI(E) access.
*/
if (fsl_pcib_decode_win(node, sc) != 0)
goto err;
cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2);
cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN |
PCIM_CMD_PORTEN;
fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2);
/* Reset the bus. Needed for Radeon video cards. */
brctl = fsl_pcib_read_config(sc->sc_dev, 0, 0, 0,
PCIR_BRIDGECTL_1, 1);
brctl |= PCIB_BCR_SECBUS_RESET;
fsl_pcib_write_config(sc->sc_dev, 0, 0, 0,
PCIR_BRIDGECTL_1, brctl, 1);
DELAY(100000);
brctl &= ~PCIB_BCR_SECBUS_RESET;
fsl_pcib_write_config(sc->sc_dev, 0, 0, 0,
PCIR_BRIDGECTL_1, brctl, 1);
DELAY(100000);
if (sc->sc_pcie) {
ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1);
if (ltssm < LTSSM_STAT_L0) {
if (bootverbose)
printf("PCI %d: no PCIE link, skipping\n",
device_get_unit(dev));
return (0);
}
}
/* Allocate irq */
rid = 0;
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE | RF_SHAREABLE);
if (sc->sc_irq_res == NULL) {
error = fsl_pcib_detach(dev);
if (error != 0) {
device_printf(dev,
"Detach of the driver failed with error %d\n",
error);
}
return (ENXIO);
}
/* Setup interrupt handler */
error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
NULL, fsl_pcib_err_intr, dev, &sc->sc_ih);
if (error != 0) {
device_printf(dev, "Could not setup irq, %d\n", error);
sc->sc_ih = NULL;
error = fsl_pcib_detach(dev);
if (error != 0) {
device_printf(dev,
"Detach of the driver failed with error %d\n",
error);
}
return (ENXIO);
}
fsl_pcib_err_init(dev);
return (ofw_pci_attach(dev));
err:
return (ENXIO);
}
static uint32_t
fsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func,
u_int reg, int bytes)
{
uint32_t addr, data;
addr = CONFIG_ACCESS_ENABLE;
addr |= (bus & 0xff) << 16;
addr |= (slot & 0x1f) << 11;
addr |= (func & 0x7) << 8;
addr |= reg & 0xfc;
if (sc->sc_pcie)
addr |= (reg & 0xf00) << 16;
mtx_lock_spin(&sc->sc_cfg_mtx);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr);
switch (bytes) {
case 1:
data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
REG_CFG_DATA + (reg & 3));
break;
case 2:
data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh,
REG_CFG_DATA + (reg & 2)));
break;
case 4:
data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
REG_CFG_DATA));
break;
default:
data = ~0;
break;
}
mtx_unlock_spin(&sc->sc_cfg_mtx);
return (data);
}
static void
fsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func,
u_int reg, uint32_t data, int bytes)
{
uint32_t addr;
addr = CONFIG_ACCESS_ENABLE;
addr |= (bus & 0xff) << 16;
addr |= (slot & 0x1f) << 11;
addr |= (func & 0x7) << 8;
addr |= reg & 0xfc;
if (sc->sc_pcie)
addr |= (reg & 0xf00) << 16;
mtx_lock_spin(&sc->sc_cfg_mtx);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr);
switch (bytes) {
case 1:
bus_space_write_1(sc->sc_bst, sc->sc_bsh,
REG_CFG_DATA + (reg & 3), data);
break;
case 2:
bus_space_write_2(sc->sc_bst, sc->sc_bsh,
REG_CFG_DATA + (reg & 2), htole16(data));
break;
case 4:
bus_space_write_4(sc->sc_bst, sc->sc_bsh,
REG_CFG_DATA, htole32(data));
break;
}
mtx_unlock_spin(&sc->sc_cfg_mtx);
}
#if 0
static void
dump(struct fsl_pcib_softc *sc)
{
unsigned int i;
#define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o)
for (i = 0; i < 5; i++) {
printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i)));
printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i)));
printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i)));
printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i)));
}
printf("\n");
for (i = 1; i < 4; i++) {
printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i)));
printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i)));
printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i)));
printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i)));
}
printf("\n");
#undef RD
for (i = 0; i < 0x48; i += 4) {
printf("cfg%02x=0x%08x\n", i, fsl_pcib_cfgread(sc, 0, 0, 0,
i, 4));
}
}
#endif
static int
fsl_pcib_maxslots(device_t dev)
{
struct fsl_pcib_softc *sc = device_get_softc(dev);
return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX);
}
static uint32_t
fsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
u_int reg, int bytes)
{
struct fsl_pcib_softc *sc = device_get_softc(dev);
u_int devfn;
if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10)
return (~0);
devfn = DEVFN(bus, slot, func);
return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes));
}
static void
fsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
u_int reg, uint32_t val, int bytes)
{
struct fsl_pcib_softc *sc = device_get_softc(dev);
if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10)
return;
fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes);
}
static void
fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, uint64_t start,
uint64_t size, uint64_t pci_start)
{
uint32_t attr, bar, tar;
KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__));
attr = PIWAR_EN;
switch (tgt) {
case -1:
attr &= ~PIWAR_EN;
break;
case PIWAR_TRGT_LOCAL:
attr |= (ffsl(size) - 2);
default:
attr |= (tgt << PIWAR_TRGT_S);
break;
}
tar = start >> 12;
bar = pci_start >> 12;
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr);
}
static void
fsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, uint64_t start,
uint64_t size, uint64_t pci_start)
{
uint32_t attr, bar, tar;
switch (res) {
case SYS_RES_MEMORY:
attr = 0x80044000 | (ffsll(size) - 2);
break;
case SYS_RES_IOPORT:
attr = 0x80088000 | (ffsll(size) - 2);
break;
default:
attr = 0x0004401f;
break;
}
bar = start >> 12;
tar = pci_start >> 12;
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr);
}
static void
fsl_pcib_err_init(device_t dev)
{
struct fsl_pcib_softc *sc;
uint16_t sec_stat, dsr;
uint32_t dcr, err_en;
sc = device_get_softc(dev);
sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2);
if (sec_stat)
fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2);
if (sc->sc_pcie) {
/* Clear error bits */
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER,
0xffffffff);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR,
0xffffffff);
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR,
0xffffffff);
dsr = fsl_pcib_cfgread(sc, 0, 0, 0,
sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2);
if (dsr)
fsl_pcib_cfgwrite(sc, 0, 0, 0,
sc->sc_pcie_capreg + PCIER_DEVICE_STA,
0xffff, 2);
/* Enable all errors reporting */
err_en = 0x00bfff00;
bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN,
err_en);
/* Enable error reporting: URR, FER, NFER */
dcr = fsl_pcib_cfgread(sc, 0, 0, 0,
sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4);
dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE |
PCIEM_CTL_NFER_ENABLE;
fsl_pcib_cfgwrite(sc, 0, 0, 0,
sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4);
}
}
static int
fsl_pcib_detach(device_t dev)
{
struct fsl_pcib_softc *sc;
sc = device_get_softc(dev);
mtx_destroy(&sc->sc_cfg_mtx);
return (bus_generic_detach(dev));
}
static int
fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc)
{
device_t dev;
int error, i, trgt;
dev = sc->sc_dev;
fsl_pcib_outbound(sc, 0, -1, 0, 0, 0);
/*
* Configure LAW decode windows.
*/
error = law_pci_target(sc->sc_res, &sc->sc_iomem_target,
&sc->sc_ioport_target);
if (error != 0) {
device_printf(dev, "could not retrieve PCI LAW target info\n");
return (error);
}
for (i = 0; i < sc->pci_sc.sc_nrange; i++) {
switch (sc->pci_sc.sc_range[i].pci_hi &
OFW_PCI_PHYS_HI_SPACEMASK) {
case OFW_PCI_PHYS_HI_SPACE_CONFIG:
continue;
case OFW_PCI_PHYS_HI_SPACE_IO:
trgt = sc->sc_ioport_target;
fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT,
sc->pci_sc.sc_range[i].host,
sc->pci_sc.sc_range[i].size,
sc->pci_sc.sc_range[i].pci);
sc->sc_ioport_start = sc->pci_sc.sc_range[i].pci;
sc->sc_ioport_end = sc->pci_sc.sc_range[i].pci +
sc->pci_sc.sc_range[i].size - 1;
break;
case OFW_PCI_PHYS_HI_SPACE_MEM32:
case OFW_PCI_PHYS_HI_SPACE_MEM64:
trgt = sc->sc_iomem_target;
fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY,
sc->pci_sc.sc_range[i].host,
sc->pci_sc.sc_range[i].size,
sc->pci_sc.sc_range[i].pci);
sc->sc_iomem_start = sc->pci_sc.sc_range[i].pci;
sc->sc_iomem_end = sc->pci_sc.sc_range[i].pci +
sc->pci_sc.sc_range[i].size - 1;
break;
default:
panic("Unknown range type %#x\n",
sc->pci_sc.sc_range[i].pci_hi &
OFW_PCI_PHYS_HI_SPACEMASK);
}
error = law_enable(trgt, sc->pci_sc.sc_range[i].host,
sc->pci_sc.sc_range[i].size);
if (error != 0) {
device_printf(dev, "could not program LAW for range "
"%d\n", i);
return (error);
}
}
/*
* Set outbout and inbound windows.
*/
fsl_pcib_outbound(sc, 3, -1, 0, 0, 0);
fsl_pcib_outbound(sc, 4, -1, 0, 0, 0);
fsl_pcib_inbound(sc, 1, -1, 0, 0, 0);
fsl_pcib_inbound(sc, 2, -1, 0, 0, 0);
fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0,
ptoa(Maxmem), 0);
/* Direct-map the CCSR for MSIs. */
/* Freescale PCIe 2.x has a dedicated MSI window. */
/* inbound window 8 makes it hit 0xD00 offset, the MSI window. */
if (sc->sc_ip_maj >= 2)
fsl_pcib_inbound(sc, 8, PIWAR_TRGT_CCSR, ccsrbar_pa,
ccsrbar_size, ccsrbar_pa);
else
fsl_pcib_inbound(sc, 1, PIWAR_TRGT_CCSR, ccsrbar_pa,
ccsrbar_size, ccsrbar_pa);
return (0);
}
static int fsl_pcib_alloc_msi(device_t dev, device_t child,
int count, int maxcount, int *irqs)
{
struct fsl_pcib_softc *sc;
vmem_addr_t start;
int err, i;
sc = device_get_softc(dev);
if (msi_vmem == NULL)
return (ENODEV);
err = vmem_xalloc(msi_vmem, count, powerof2(count), 0, 0,
VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start);
if (err)
return (err);
for (i = 0; i < count; i++)
irqs[i] = start + i;
return (0);
}
static int fsl_pcib_release_msi(device_t dev, device_t child,
int count, int *irqs)
{
if (msi_vmem == NULL)
return (ENODEV);
vmem_xfree(msi_vmem, irqs[0], count);
return (0);
}
static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq)
{
return (fsl_pcib_alloc_msi(dev, child, 1, 1, irq));
}
static int fsl_pcib_release_msix(device_t dev, device_t child, int irq)
{
return (fsl_pcib_release_msi(dev, child, 1, &irq));
}
static int fsl_pcib_map_msi(device_t dev, device_t child,
int irq, uint64_t *addr, uint32_t *data)
{
struct fsl_msi_map *mp;
SLIST_FOREACH(mp, &fsl_msis, slist) {
if (irq >= mp->irq_base && irq < mp->irq_base + FSL_NUM_MSIS)
break;
}
if (mp == NULL)
return (ENODEV);
*data = (irq & 255);
*addr = ccsrbar_pa + mp->target;
return (0);
}
/*
* Linux device trees put the msi@<x> as children of the SoC, with ranges based
* on the CCSR. Since rman doesn't permit overlapping or sub-ranges between
* devices (bus_space_subregion(9) could do it, but let's not touch the PIC
* driver just to allocate a subregion for a sibling driver). This driver will
* use ccsr_write() and ccsr_read() instead.
*/
#define FSL_NUM_IRQS 8
#define FSL_NUM_MSI_PER_IRQ 32
#define FSL_MSI_TARGET 0x140
struct fsl_msi_softc {
vm_offset_t sc_base;
vm_offset_t sc_target;
int sc_msi_base_irq;
struct fsl_msi_map sc_map;
struct fsl_msi_irq {
/* This struct gets passed as the filter private data. */
struct fsl_msi_softc *sc_ptr; /* Pointer back to softc. */
struct resource *res;
int irq;
void *cookie;
int vectors[FSL_NUM_MSI_PER_IRQ];
vm_offset_t reg;
} sc_msi_irq[FSL_NUM_IRQS];
};
static int
fsl_msi_intr_filter(void *priv)
{
struct fsl_msi_irq *data = priv;
uint32_t reg;
int i;
reg = ccsr_read4(ccsrbar_va + data->reg);
i = 0;
while (reg != 0) {
if (reg & 1)
powerpc_dispatch_intr(data->vectors[i], NULL);
reg >>= 1;
i++;
}
return (FILTER_HANDLED);
}
static int
fsl_msi_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "fsl,mpic-msi"))
return (ENXIO);
device_set_desc(dev, "Freescale MSI");
return (BUS_PROBE_DEFAULT);
}
static int
fsl_msi_attach(device_t dev)
{
struct fsl_msi_softc *sc;
struct fsl_msi_irq *irq;
int i;
sc = device_get_softc(dev);
if (msi_vmem == NULL)
msi_vmem = vmem_create("MPIC MSI", 0, 0, 1, 0, M_BESTFIT | M_WAITOK);
/* Manually play with resource entries. */
sc->sc_base = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
sc->sc_map.target = bus_get_resource_start(dev, SYS_RES_MEMORY, 1);
if (sc->sc_map.target == 0)
sc->sc_map.target = sc->sc_base + FSL_MSI_TARGET;
for (i = 0; i < FSL_NUM_IRQS; i++) {
irq = &sc->sc_msi_irq[i];
irq->irq = i;
irq->reg = sc->sc_base + 16 * i;
irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&irq->irq, RF_ACTIVE);
bus_setup_intr(dev, irq->res, INTR_TYPE_MISC | INTR_MPSAFE,
fsl_msi_intr_filter, NULL, irq, &irq->cookie);
}
sc->sc_map.irq_base = powerpc_register_pic(dev, ofw_bus_get_node(dev),
FSL_NUM_MSIS, 0, 0);
/* Let vmem and the IRQ subsystem work their magic for allocations. */
vmem_add(msi_vmem, sc->sc_map.irq_base, FSL_NUM_MSIS, M_WAITOK);
SLIST_INSERT_HEAD(&fsl_msis, &sc->sc_map, slist);
return (0);
}
static void
fsl_msi_enable(device_t dev, u_int irq, u_int vector, void **priv)
{
struct fsl_msi_softc *sc;
struct fsl_msi_irq *irqd;
sc = device_get_softc(dev);
irqd = &sc->sc_msi_irq[irq / FSL_NUM_MSI_PER_IRQ];
irqd->vectors[irq % FSL_NUM_MSI_PER_IRQ] = vector;
}
static device_method_t fsl_msi_methods[] = {
DEVMETHOD(device_probe, fsl_msi_probe),
DEVMETHOD(device_attach, fsl_msi_attach),
DEVMETHOD(pic_enable, fsl_msi_enable),
DEVMETHOD_END
};
static devclass_t fsl_msi_devclass;
static driver_t fsl_msi_driver = {
"fsl_msi",
fsl_msi_methods,
sizeof(struct fsl_msi_softc)
};
EARLY_DRIVER_MODULE(fsl_msi, simplebus, fsl_msi_driver, fsl_msi_devclass, 0, 0,
BUS_PASS_INTERRUPT + 1);

View File

@ -0,0 +1,111 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* Copyright 2015 Justin Hibbits
* 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 ``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 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/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ktr.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rman.h>
#include <sys/endian.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <dev/ofw/openfirm.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 <machine/intr_machdep.h>
#include <rtems/bsd/local/pcib_if.h>
DECLARE_CLASS(ofw_pcib_pci_driver);
struct fsl_pcib_softc {
/*
* This is here so that we can use pci bridge methods, too - the
* generic routines only need the dev, secbus and subbus members
* filled.
*
* XXX: This should be extracted from ofw_pcib_pci.c, and shared in a
* header.
*/
struct pcib_softc ops_pcib_sc;
phandle_t ops_node;
struct ofw_bus_iinfo ops_iinfo;
};
static int
fsl_pcib_rc_probe(device_t dev)
{
if (pci_get_vendor(dev) != 0x1957)
return (ENXIO);
if (pci_get_progif(dev) != 0)
return (ENXIO);
if (pci_get_class(dev) != PCIC_PROCESSOR)
return (ENXIO);
if (pci_get_subclass(dev) != PCIS_PROCESSOR_POWERPC)
return (ENXIO);
device_set_desc(dev, "MPC85xx Root Complex bridge");
return (BUS_PROBE_DEFAULT);
}
static device_method_t fsl_pcib_rc_methods[] = {
DEVMETHOD(device_probe, fsl_pcib_rc_probe),
DEVMETHOD_END
};
static devclass_t fsl_pcib_rc_devclass;
DEFINE_CLASS_1(pcib, fsl_pcib_rc_driver, fsl_pcib_rc_methods,
sizeof(struct fsl_pcib_softc), ofw_pcib_pci_driver);
EARLY_DRIVER_MODULE(rcpcib, pci, fsl_pcib_rc_driver, fsl_pcib_rc_devclass, 0, 0,
BUS_PASS_BUS);

View File

@ -0,0 +1,710 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2008-2012 Semihalf.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <rtems/bsd/local/opt_platform.h>
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
#include <sys/smp.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/hid.h>
#include <machine/_inttypes.h>
#include <machine/machdep.h>
#include <machine/md_var.h>
#include <machine/platform.h>
#include <machine/platformvar.h>
#include <machine/smp.h>
#include <machine/spr.h>
#include <machine/vmparam.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_extern.h>
#include <powerpc/mpc85xx/mpc85xx.h>
#include <rtems/bsd/local/platform_if.h>
#ifdef SMP
extern void *ap_pcpu;
extern vm_paddr_t kernload; /* Kernel physical load address */
extern uint8_t __boot_page[]; /* Boot page body */
extern uint32_t bp_kernload;
extern vm_offset_t __startkernel;
struct cpu_release {
uint32_t entry_h;
uint32_t entry_l;
uint32_t r3_h;
uint32_t r3_l;
uint32_t reserved;
uint32_t pir;
};
#endif
extern uint32_t *bootinfo;
vm_paddr_t ccsrbar_pa;
vm_offset_t ccsrbar_va;
vm_size_t ccsrbar_size;
static int cpu, maxcpu;
static device_t rcpm_dev;
static void dummy_freeze(device_t, bool);
static void (*freeze_timebase)(device_t, bool) = dummy_freeze;
static int mpc85xx_probe(platform_t);
static void mpc85xx_mem_regions(platform_t, struct mem_region *phys,
int *physsz, struct mem_region *avail, int *availsz);
static u_long mpc85xx_timebase_freq(platform_t, struct cpuref *cpuref);
static int mpc85xx_smp_first_cpu(platform_t, struct cpuref *cpuref);
static int mpc85xx_smp_next_cpu(platform_t, struct cpuref *cpuref);
static int mpc85xx_smp_get_bsp(platform_t, struct cpuref *cpuref);
static int mpc85xx_smp_start_cpu(platform_t, struct pcpu *cpu);
static void mpc85xx_smp_timebase_sync(platform_t, u_long tb, int ap);
static void mpc85xx_reset(platform_t);
static platform_method_t mpc85xx_methods[] = {
PLATFORMMETHOD(platform_probe, mpc85xx_probe),
PLATFORMMETHOD(platform_attach, mpc85xx_attach),
PLATFORMMETHOD(platform_mem_regions, mpc85xx_mem_regions),
PLATFORMMETHOD(platform_timebase_freq, mpc85xx_timebase_freq),
PLATFORMMETHOD(platform_smp_first_cpu, mpc85xx_smp_first_cpu),
PLATFORMMETHOD(platform_smp_next_cpu, mpc85xx_smp_next_cpu),
PLATFORMMETHOD(platform_smp_get_bsp, mpc85xx_smp_get_bsp),
PLATFORMMETHOD(platform_smp_start_cpu, mpc85xx_smp_start_cpu),
PLATFORMMETHOD(platform_smp_timebase_sync, mpc85xx_smp_timebase_sync),
PLATFORMMETHOD(platform_reset, mpc85xx_reset),
PLATFORMMETHOD_END
};
DEFINE_CLASS_0(mpc85xx, mpc85xx_platform, mpc85xx_methods, 0);
PLATFORM_DEF(mpc85xx_platform);
static int
mpc85xx_probe(platform_t plat)
{
u_int pvr = (mfpvr() >> 16) & 0xFFFF;
switch (pvr) {
case FSL_E500v1:
case FSL_E500v2:
case FSL_E500mc:
case FSL_E5500:
case FSL_E6500:
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
int
mpc85xx_attach(platform_t plat)
{
phandle_t cpus, child, ccsr;
const char *soc_name_guesses[] = {"/soc", "soc", NULL};
const char **name;
pcell_t ranges[6], acells, pacells, scells;
uint64_t ccsrbar, ccsrsize;
int i;
if ((cpus = OF_finddevice("/cpus")) != -1) {
for (maxcpu = 0, child = OF_child(cpus); child != 0;
child = OF_peer(child), maxcpu++)
;
} else
maxcpu = 1;
/*
* Locate CCSR region. Irritatingly, there is no way to find it
* unless you already know where it is. Try to infer its location
* from the device tree.
*/
ccsr = -1;
for (name = soc_name_guesses; *name != NULL && ccsr == -1; name++)
ccsr = OF_finddevice(*name);
if (ccsr == -1) {
char type[64];
/* That didn't work. Search for devices of type "soc" */
child = OF_child(OF_peer(0));
for (OF_child(child); child != 0; child = OF_peer(child)) {
if (OF_getprop(child, "device_type", type, sizeof(type))
<= 0)
continue;
if (strcmp(type, "soc") == 0) {
ccsr = child;
break;
}
}
}
if (ccsr == -1)
panic("Could not locate CCSR window!");
OF_getprop(ccsr, "#size-cells", &scells, sizeof(scells));
OF_getprop(ccsr, "#address-cells", &acells, sizeof(acells));
OF_searchprop(OF_parent(ccsr), "#address-cells", &pacells,
sizeof(pacells));
OF_getprop(ccsr, "ranges", ranges, sizeof(ranges));
ccsrbar = ccsrsize = 0;
for (i = acells; i < acells + pacells; i++) {
ccsrbar <<= 32;
ccsrbar |= ranges[i];
}
for (i = acells + pacells; i < acells + pacells + scells; i++) {
ccsrsize <<= 32;
ccsrsize |= ranges[i];
}
ccsrbar_va = pmap_early_io_map(ccsrbar, ccsrsize);
ccsrbar_pa = ccsrbar;
ccsrbar_size = ccsrsize;
mpc85xx_enable_l3_cache();
return (0);
}
void
mpc85xx_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
struct mem_region *avail, int *availsz)
{
ofw_mem_regions(phys, physsz, avail, availsz);
}
static u_long
mpc85xx_timebase_freq(platform_t plat, struct cpuref *cpuref)
{
u_long ticks;
phandle_t cpus, child;
pcell_t freq;
if (bootinfo != NULL) {
if (bootinfo[0] == 1) {
/* Backward compatibility. See 8-STABLE. */
ticks = bootinfo[3] >> 3;
} else {
/* Compatibility with Juniper's loader. */
ticks = bootinfo[5] >> 3;
}
} else
ticks = 0;
if ((cpus = OF_finddevice("/cpus")) == -1)
goto out;
if ((child = OF_child(cpus)) == 0)
goto out;
switch (OF_getproplen(child, "timebase-frequency")) {
case 4:
{
uint32_t tbase;
OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase));
ticks = tbase;
return (ticks);
}
case 8:
{
uint64_t tbase;
OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase));
ticks = tbase;
return (ticks);
}
default:
break;
}
freq = 0;
if (OF_getprop(child, "bus-frequency", (void *)&freq,
sizeof(freq)) <= 0)
goto out;
if (freq == 0)
goto out;
/*
* Time Base and Decrementer are updated every 8 CCB bus clocks.
* HID0[SEL_TBCLK] = 0
*/
if (mpc85xx_is_qoriq())
ticks = freq / 32;
else
ticks = freq / 8;
out:
if (ticks <= 0)
panic("Unable to determine timebase frequency!");
return (ticks);
}
static int
mpc85xx_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
{
cpu = 0;
cpuref->cr_cpuid = cpu;
cpuref->cr_hwref = cpuref->cr_cpuid;
if (bootverbose)
printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid);
cpu++;
return (0);
}
static int
mpc85xx_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
{
if (cpu >= maxcpu)
return (ENOENT);
cpuref->cr_cpuid = cpu++;
cpuref->cr_hwref = cpuref->cr_cpuid;
if (bootverbose)
printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid);
return (0);
}
static int
mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
{
cpuref->cr_cpuid = mfspr(SPR_PIR);
cpuref->cr_hwref = cpuref->cr_cpuid;
return (0);
}
#ifdef SMP
static int
mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc)
{
vm_paddr_t rel_pa, bptr;
volatile struct cpu_release *rel;
vm_offset_t rel_va, rel_page;
phandle_t node;
int i;
/* If we're calling this, the node already exists. */
node = OF_finddevice("/cpus");
for (i = 0, node = OF_child(node); i < pc->pc_cpuid;
i++, node = OF_peer(node))
;
if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa,
sizeof(rel_pa)) == -1) {
return (ENOENT);
}
rel_page = kva_alloc(PAGE_SIZE);
if (rel_page == 0)
return (ENOMEM);
critical_enter();
rel_va = rel_page + (rel_pa & PAGE_MASK);
pmap_kenter(rel_page, rel_pa & ~PAGE_MASK);
rel = (struct cpu_release *)rel_va;
bptr = pmap_kextract((uintptr_t)__boot_page);
cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));
rel->pir = pc->pc_cpuid; __asm __volatile("sync");
rel->entry_h = (bptr >> 32);
rel->entry_l = bptr; __asm __volatile("sync");
cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel));
if (bootverbose)
printf("Waking up CPU %d via CPU release page %p\n",
pc->pc_cpuid, rel);
critical_exit();
pmap_kremove(rel_page);
kva_free(rel_page, PAGE_SIZE);
return (0);
}
#endif
static int
mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc)
{
#ifdef SMP
vm_paddr_t bptr;
uint32_t reg;
int timeout;
uintptr_t brr;
int cpuid;
int epapr_boot = 0;
uint32_t tgt;
if (mpc85xx_is_qoriq()) {
reg = ccsr_read4(OCP85XX_COREDISR);
cpuid = pc->pc_cpuid;
if ((reg & (1 << cpuid)) != 0) {
printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid);
return (-1);
}
brr = OCP85XX_BRR;
} else {
brr = OCP85XX_EEBPCR;
cpuid = pc->pc_cpuid + 24;
}
bp_kernload = kernload;
/*
* bp_kernload is in the boot page. Sync the cache because ePAPR
* booting has the other core(s) already running.
*/
cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload));
ap_pcpu = pc;
__asm __volatile("msync; isync");
/* First try the ePAPR way. */
if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) {
epapr_boot = 1;
goto spin_wait;
}
reg = ccsr_read4(brr);
if ((reg & (1 << cpuid)) != 0) {
printf("SMP: CPU %d already out of hold-off state!\n",
pc->pc_cpuid);
return (ENXIO);
}
/* Flush caches to have our changes hit DRAM. */
cpu_flush_dcache(__boot_page, 4096);
bptr = pmap_kextract((uintptr_t)__boot_page);
KASSERT((bptr & 0xfff) == 0,
("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr));
if (mpc85xx_is_qoriq()) {
/*
* Read DDR controller configuration to select proper BPTR target ID.
*
* On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers
* interleaving. If this bit is set, we have to use
* OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs,
* this bit is reserved and always 0.
*/
reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG);
if (reg & (1 << 29))
tgt = OCP85XX_TGTIF_RAM_INTL;
else
tgt = OCP85XX_TGTIF_RAM1;
/*
* Set BSTR to the physical address of the boot page
*/
ccsr_write4(OCP85XX_BSTRH, bptr >> 32);
ccsr_write4(OCP85XX_BSTRL, bptr);
ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK |
(tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2));
/* Read back OCP85XX_BSTAR to synchronize write */
ccsr_read4(OCP85XX_BSTAR);
/*
* Enable and configure time base on new CPU.
*/
/* Set TB clock source to platform clock / 32 */
reg = ccsr_read4(CCSR_CTBCKSELR);
ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid));
/* Enable TB */
reg = ccsr_read4(CCSR_CTBENR);
ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid));
} else {
/*
* Set BPTR to the physical address of the boot page
*/
bptr = (bptr >> 12) | 0x80000000u;
ccsr_write4(OCP85XX_BPTR, bptr);
__asm __volatile("isync; msync");
}
/*
* Release AP from hold-off state
*/
reg = ccsr_read4(brr);
ccsr_write4(brr, reg | (1 << cpuid));
__asm __volatile("isync; msync");
spin_wait:
timeout = 500;
while (!pc->pc_awake && timeout--)
DELAY(1000); /* wait 1ms */
/*
* Disable boot page translation so that the 4K page at the default
* address (= 0xfffff000) isn't permanently remapped and thus not
* usable otherwise.
*/
if (!epapr_boot) {
if (mpc85xx_is_qoriq())
ccsr_write4(OCP85XX_BSTAR, 0);
else
ccsr_write4(OCP85XX_BPTR, 0);
__asm __volatile("isync; msync");
}
if (!pc->pc_awake)
panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid);
return ((pc->pc_awake) ? 0 : EBUSY);
#else
/* No SMP support */
return (ENXIO);
#endif
}
static void
mpc85xx_reset(platform_t plat)
{
/*
* Try the dedicated reset register first.
* If the SoC doesn't have one, we'll fall
* back to using the debug control register.
*/
ccsr_write4(OCP85XX_RSTCR, 2);
/* Clear DBCR0, disables debug interrupts and events. */
mtspr(SPR_DBCR0, 0);
__asm __volatile("isync");
/* Enable Debug Interrupts in MSR. */
mtmsr(mfmsr() | PSL_DE);
/* Enable debug interrupts and issue reset. */
mtspr(SPR_DBCR0, mfspr(SPR_DBCR0) | DBCR0_IDM | DBCR0_RST_SYSTEM);
printf("Reset failed...\n");
while (1)
;
}
static void
mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap)
{
static volatile bool tb_ready;
static volatile int cpu_done;
if (ap) {
/* APs. Hold off until we get a stable timebase. */
while (!tb_ready)
atomic_thread_fence_seq_cst();
mttb(tb);
atomic_add_int(&cpu_done, 1);
while (cpu_done < mp_ncpus)
atomic_thread_fence_seq_cst();
} else {
/* BSP */
freeze_timebase(rcpm_dev, true);
tb_ready = true;
mttb(tb);
atomic_add_int(&cpu_done, 1);
while (cpu_done < mp_ncpus)
atomic_thread_fence_seq_cst();
freeze_timebase(rcpm_dev, false);
}
}
/* Fallback freeze. In case no real handler is found in the device tree. */
static void
dummy_freeze(device_t dev, bool freeze)
{
/* Nothing to do here, move along. */
}
/* QorIQ Run control/power management timebase management. */
#define RCPM_CTBENR 0x00000084
struct mpc85xx_rcpm_softc {
struct resource *sc_mem;
};
static void
mpc85xx_rcpm_freeze_timebase(device_t dev, bool freeze)
{
struct mpc85xx_rcpm_softc *sc;
sc = device_get_softc(dev);
if (freeze)
bus_write_4(sc->sc_mem, RCPM_CTBENR, 0);
else
bus_write_4(sc->sc_mem, RCPM_CTBENR, (1 << maxcpu) - 1);
}
static int
mpc85xx_rcpm_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "fsl,qoriq-rcpm-1.0"))
return (ENXIO);
device_set_desc(dev, "QorIQ Run control and power management");
return (BUS_PROBE_GENERIC);
}
static int
mpc85xx_rcpm_attach(device_t dev)
{
struct mpc85xx_rcpm_softc *sc;
int rid;
sc = device_get_softc(dev);
freeze_timebase = mpc85xx_rcpm_freeze_timebase;
rcpm_dev = dev;
rid = 0;
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE | RF_SHAREABLE);
return (0);
}
static device_method_t mpc85xx_rcpm_methods[] = {
DEVMETHOD(device_probe, mpc85xx_rcpm_probe),
DEVMETHOD(device_attach, mpc85xx_rcpm_attach),
DEVMETHOD_END
};
static devclass_t mpc85xx_rcpm_devclass;
static driver_t mpc85xx_rcpm_driver = {
"rcpm",
mpc85xx_rcpm_methods,
sizeof(struct mpc85xx_rcpm_softc)
};
EARLY_DRIVER_MODULE(mpc85xx_rcpm, simplebus, mpc85xx_rcpm_driver,
mpc85xx_rcpm_devclass, 0, 0, BUS_PASS_BUS);
/* "Global utilities" power management/Timebase management. */
#define GUTS_DEVDISR 0x00000070
#define DEVDISR_TB0 0x00004000
#define DEVDISR_TB1 0x00001000
struct mpc85xx_guts_softc {
struct resource *sc_mem;
};
static void
mpc85xx_guts_freeze_timebase(device_t dev, bool freeze)
{
struct mpc85xx_guts_softc *sc;
uint32_t devdisr;
sc = device_get_softc(dev);
devdisr = bus_read_4(sc->sc_mem, GUTS_DEVDISR);
if (freeze)
bus_write_4(sc->sc_mem, GUTS_DEVDISR,
devdisr | (DEVDISR_TB0 | DEVDISR_TB1));
else
bus_write_4(sc->sc_mem, GUTS_DEVDISR,
devdisr & ~(DEVDISR_TB0 | DEVDISR_TB1));
}
static int
mpc85xx_guts_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "fsl,mpc8572-guts") &&
!ofw_bus_is_compatible(dev, "fsl,p1020-guts") &&
!ofw_bus_is_compatible(dev, "fsl,p1021-guts") &&
!ofw_bus_is_compatible(dev, "fsl,p1022-guts") &&
!ofw_bus_is_compatible(dev, "fsl,p1023-guts") &&
!ofw_bus_is_compatible(dev, "fsl,p2020-guts"))
return (ENXIO);
device_set_desc(dev, "MPC85xx Global Utilities");
return (BUS_PROBE_GENERIC);
}
static int
mpc85xx_guts_attach(device_t dev)
{
struct mpc85xx_rcpm_softc *sc;
int rid;
sc = device_get_softc(dev);
freeze_timebase = mpc85xx_guts_freeze_timebase;
rcpm_dev = dev;
rid = 0;
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE | RF_SHAREABLE);
return (0);
}
static device_method_t mpc85xx_guts_methods[] = {
DEVMETHOD(device_probe, mpc85xx_guts_probe),
DEVMETHOD(device_attach, mpc85xx_guts_attach),
DEVMETHOD_END
};
static driver_t mpc85xx_guts_driver = {
"guts",
mpc85xx_guts_methods,
sizeof(struct mpc85xx_guts_softc)
};
static devclass_t mpc85xx_guts_devclass;
EARLY_DRIVER_MODULE(mpc85xx_guts, simplebus, mpc85xx_guts_driver,
mpc85xx_guts_devclass, 0, 0, BUS_PASS_BUS);

View File

@ -0,0 +1,178 @@
#include <machine/rtems-bsd-kernel-space.h>
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2000 BSDi
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <dev/ofw/openfirm.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 <machine/intr_machdep.h>
#include <rtems/bsd/local/pcib_if.h>
static int ofw_pcib_pci_probe(device_t bus);
static int ofw_pcib_pci_attach(device_t bus);
static phandle_t ofw_pcib_pci_get_node(device_t bus, device_t dev);
static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev,
int intpin);
static device_method_t ofw_pcib_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ofw_pcib_pci_probe),
DEVMETHOD(device_attach, ofw_pcib_pci_attach),
/* pcib interface */
DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt),
DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node),
DEVMETHOD_END
};
static devclass_t pcib_devclass;
struct ofw_pcib_softc {
/*
* This is here so that we can use pci bridge methods, too - the
* generic routines only need the dev, secbus and subbus members
* filled.
*/
struct pcib_softc ops_pcib_sc;
phandle_t ops_node;
struct ofw_bus_iinfo ops_iinfo;
};
DEFINE_CLASS_1(pcib, ofw_pcib_pci_driver, ofw_pcib_pci_methods,
sizeof(struct ofw_pcib_softc), pcib_driver);
EARLY_DRIVER_MODULE(ofw_pcib, pci, ofw_pcib_pci_driver, pcib_devclass, 0, 0,
BUS_PASS_BUS);
static int
ofw_pcib_pci_probe(device_t dev)
{
if ((pci_get_class(dev) != PCIC_BRIDGE) ||
(pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) {
return (ENXIO);
}
if (ofw_bus_get_node(dev) == -1)
return (ENXIO);
device_set_desc(dev, "OFW PCI-PCI bridge");
return (0);
}
static int
ofw_pcib_pci_attach(device_t dev)
{
struct ofw_pcib_softc *sc;
sc = device_get_softc(dev);
sc->ops_pcib_sc.dev = dev;
sc->ops_node = ofw_bus_get_node(dev);
ofw_bus_setup_iinfo(sc->ops_node, &sc->ops_iinfo,
sizeof(cell_t));
pcib_attach_common(dev);
return (pcib_attach_child(dev));
}
static phandle_t
ofw_pcib_pci_get_node(device_t bridge, device_t dev)
{
/* We have only one child, the PCI bus, so pass it our node */
return (ofw_bus_get_node(bridge));
}
static int
ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin)
{
struct ofw_pcib_softc *sc;
struct ofw_bus_iinfo *ii;
struct ofw_pci_register reg;
cell_t pintr, mintr[2];
int intrcells;
phandle_t iparent;
sc = device_get_softc(bridge);
ii = &sc->ops_iinfo;
if (ii->opi_imapsz > 0) {
pintr = intpin;
/* Fabricate imap information if 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);
intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, &reg,
sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
&iparent);
if (intrcells) {
/*
* If we've found a mapping, return it and don't map
* it again on higher levels - that causes problems
* in some cases, and never seems to be required.
*/
mintr[0] = ofw_bus_map_intr(dev, iparent, intrcells,
mintr);
return (mintr[0]);
}
} else if (intpin >= 1 && intpin <= 4) {
/*
* When an interrupt map is missing, we need to do the
* standard PCI swizzle and continue mapping at the parent.
*/
return (pcib_route_interrupt(bridge, dev, intpin));
}
return (PCIB_ROUTE_INTERRUPT(device_get_parent(device_get_parent(
bridge)), bridge, intpin));
}