5a646f0eca
Submitted by: yongari
433 lines
12 KiB
C
433 lines
12 KiB
C
/*-
|
|
* Copyright (c) 2007 Bruce M. Simpson.
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Child driver for PCI host bridge core.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/endian.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/vm_extern.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/pcb.h>
|
|
#include <machine/pmap.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcib_private.h>
|
|
|
|
#include "pcib_if.h"
|
|
|
|
#include <dev/siba/siba_ids.h>
|
|
#include <dev/siba/sibareg.h>
|
|
#include <dev/siba/sibavar.h>
|
|
#include <dev/siba/siba_pcibvar.h>
|
|
|
|
#ifndef MIPS_MEM_RID
|
|
#define MIPS_MEM_RID 0x20
|
|
#endif
|
|
|
|
#define SBPCI_SLOTMAX 15
|
|
|
|
#define SBPCI_READ_4(sc, reg) \
|
|
bus_space_write_4((sc)->sc_bt, (sc)->sc_bh, (reg))
|
|
|
|
#define SBPCI_WRITE_4(sc, reg, val) \
|
|
bus_space_write_4((sc)->sc_bt, (sc)->sc_bh, (reg), (val))
|
|
|
|
/*
|
|
* PCI Configuration space window (64MB).
|
|
* contained in SBTOPCI1 window.
|
|
*/
|
|
#define SBPCI_CFGBASE 0x0C000000
|
|
#define SBPCI_CFGSIZE 0x01000000
|
|
|
|
/*
|
|
* TODO: implement type 1 config space access (ie beyond bus 0)
|
|
* we may need to tweak the windows to do this
|
|
* TODO: interrupt routing.
|
|
* TODO: fully implement bus allocation.
|
|
* TODO: implement resource managers.
|
|
* TODO: code cleanup.
|
|
*/
|
|
|
|
static int siba_pcib_activate_resource(device_t, device_t, int,
|
|
int, struct resource *);
|
|
static struct resource *
|
|
siba_pcib_alloc_resource(device_t, device_t, int, int *,
|
|
u_long , u_long, u_long, u_int);
|
|
static int siba_pcib_attach(device_t);
|
|
static int siba_pcib_deactivate_resource(device_t, device_t, int,
|
|
int, struct resource *);
|
|
static int siba_pcib_maxslots(device_t);
|
|
static int siba_pcib_probe(device_t);
|
|
static u_int32_t
|
|
siba_pcib_read_config(device_t, u_int, u_int, u_int, u_int,
|
|
int);
|
|
static int siba_pcib_read_ivar(device_t, device_t, int, uintptr_t *);
|
|
static int siba_pcib_release_resource(device_t, device_t, int, int,
|
|
struct resource *);
|
|
static int siba_pcib_route_interrupt(device_t, device_t, int);
|
|
static int siba_pcib_setup_intr(device_t, device_t, struct resource *,
|
|
int, driver_filter_t *, driver_intr_t *, void *, void **);
|
|
static int siba_pcib_teardown_intr(device_t, device_t, struct resource *,
|
|
void *);
|
|
static void siba_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
|
|
u_int32_t, int);
|
|
static int siba_pcib_write_ivar(device_t, device_t, int, uintptr_t);
|
|
|
|
static int
|
|
siba_pcib_probe(device_t dev)
|
|
{
|
|
|
|
/* TODO: support earlier cores. */
|
|
/* TODO: Check if PCI host mode is enabled in the SPROM. */
|
|
if (siba_get_vendor(dev) == SIBA_VID_BROADCOM &&
|
|
siba_get_device(dev) == SIBA_DEVID_PCI) {
|
|
device_set_desc(dev, "SiBa-to-PCI host bridge");
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
return (ENXIO);
|
|
}
|
|
|
|
//extern int rman_debug;
|
|
|
|
static int
|
|
siba_pcib_attach(device_t dev)
|
|
{
|
|
struct siba_pcib_softc *sc = device_get_softc(dev);
|
|
int rid;
|
|
|
|
/*
|
|
* Allocate the resources which the parent bus has already
|
|
* determined for us.
|
|
*/
|
|
rid = MIPS_MEM_RID; /* XXX */
|
|
//rman_debug = 1;
|
|
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
|
RF_ACTIVE);
|
|
if (sc->sc_mem == NULL) {
|
|
device_printf(dev, "unable to allocate memory\n");
|
|
return (ENXIO);
|
|
}
|
|
|
|
sc->sc_bt = rman_get_bustag(sc->sc_mem);
|
|
sc->sc_bh = rman_get_bushandle(sc->sc_mem);
|
|
|
|
device_printf(dev, "bridge registers addr 0x%08x vaddr %p\n",
|
|
(uint32_t)sc->sc_bh, rman_get_virtual(sc->sc_mem));
|
|
|
|
SBPCI_WRITE_4(sc, 0x0000, 0x05);
|
|
SBPCI_WRITE_4(sc, 0x0000, 0x0D);
|
|
DELAY(150);
|
|
SBPCI_WRITE_4(sc, 0x0000, 0x0F);
|
|
SBPCI_WRITE_4(sc, 0x0010, 0x01);
|
|
DELAY(1);
|
|
|
|
bus_space_handle_t sc_cfg_hand;
|
|
int error;
|
|
|
|
/*
|
|
* XXX this doesn't actually do anything on mips; however... should
|
|
* we not be mapping to KSEG1? we need to wire down the range.
|
|
*/
|
|
error = bus_space_map(sc->sc_bt, SBPCI_CFGBASE, SBPCI_CFGSIZE,
|
|
0, &sc_cfg_hand);
|
|
if (error) {
|
|
device_printf(dev, "cannot map PCI configuration space\n");
|
|
return (ENXIO);
|
|
}
|
|
device_printf(dev, "mapped pci config space at 0x%08x\n",
|
|
(uint32_t)sc_cfg_hand);
|
|
|
|
/*
|
|
* Setup configuration, io, and dma space windows.
|
|
* XXX we need to be able to do type 1 too.
|
|
* we probably don't need to be able to do i/o cycles.
|
|
*/
|
|
|
|
/* I/O read/write window */
|
|
SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI0, 1);
|
|
/* type 0 configuration only */
|
|
SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI1, 2);
|
|
SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI2, 1 << 30); /* memory only */
|
|
DELAY(500);
|
|
|
|
/* XXX resource managers */
|
|
|
|
device_add_child(dev, "pci", -1);
|
|
return (bus_generic_attach(dev));
|
|
}
|
|
|
|
/* bus functions */
|
|
|
|
static int
|
|
siba_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
|
|
{
|
|
struct siba_pcib_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
switch (which) {
|
|
case PCIB_IVAR_BUS:
|
|
*result = sc->sc_bus;
|
|
return (0);
|
|
}
|
|
|
|
return (ENOENT);
|
|
}
|
|
|
|
static int
|
|
siba_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
|
|
{
|
|
struct siba_pcib_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
switch (which) {
|
|
case PCIB_IVAR_BUS:
|
|
sc->sc_bus = value;
|
|
return (0);
|
|
}
|
|
|
|
return (ENOENT);
|
|
}
|
|
|
|
static int
|
|
siba_pcib_setup_intr(device_t dev, device_t child, struct resource *ires,
|
|
int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg,
|
|
void **cookiep)
|
|
{
|
|
|
|
return (BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags,
|
|
filt, intr, arg, cookiep));
|
|
}
|
|
|
|
static int
|
|
siba_pcib_teardown_intr(device_t dev, device_t child, struct resource *vec,
|
|
void *cookie)
|
|
{
|
|
|
|
return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, vec, cookie));
|
|
}
|
|
|
|
static struct resource *
|
|
siba_pcib_alloc_resource(device_t bus, device_t child, int type, int *rid,
|
|
u_long start, u_long end, u_long count, u_int flags)
|
|
{
|
|
#if 1
|
|
|
|
//device_printf(bus, "%s: not yet implemented\n", __func__);
|
|
return (NULL);
|
|
#else
|
|
bus_space_tag_t tag;
|
|
struct siba_pcib_softc *sc = device_get_softc(bus);
|
|
struct rman *rmanp;
|
|
struct resource *rv;
|
|
|
|
tag = 0;
|
|
rv = NULL;
|
|
switch (type) {
|
|
case SYS_RES_IRQ:
|
|
rmanp = &sc->sc_irq_rman;
|
|
break;
|
|
|
|
case SYS_RES_MEMORY:
|
|
rmanp = &sc->sc_mem_rman;
|
|
tag = &sc->sc_pci_memt;
|
|
break;
|
|
|
|
default:
|
|
return (rv);
|
|
}
|
|
|
|
rv = rman_reserve_resource(rmanp, start, end, count, flags, child);
|
|
if (rv != NULL) {
|
|
rman_set_rid(rv, *rid);
|
|
if (type == SYS_RES_MEMORY) {
|
|
#if 0
|
|
rman_set_bustag(rv, tag);
|
|
rman_set_bushandle(rv, rman_get_bushandle(sc->sc_mem) +
|
|
(rman_get_start(rv) - IXP425_PCI_MEM_HWBASE));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return (rv);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
siba_pcib_activate_resource(device_t bus, device_t child, int type, int rid,
|
|
struct resource *r)
|
|
{
|
|
|
|
device_printf(bus, "%s: not yet implemented\n", __func__);
|
|
device_printf(bus, "%s called activate_resource\n",
|
|
device_get_nameunit(child));
|
|
return (ENXIO);
|
|
}
|
|
|
|
static int
|
|
siba_pcib_deactivate_resource(device_t bus, device_t child, int type, int rid,
|
|
struct resource *r)
|
|
{
|
|
|
|
device_printf(bus, "%s: not yet implemented\n", __func__);
|
|
device_printf(bus, "%s called deactivate_resource\n",
|
|
device_get_nameunit(child));
|
|
return (ENXIO);
|
|
}
|
|
|
|
static int
|
|
siba_pcib_release_resource(device_t bus, device_t child, int type, int rid,
|
|
struct resource *r)
|
|
{
|
|
|
|
device_printf(bus, "%s: not yet implemented\n", __func__);
|
|
device_printf(bus, "%s called release_resource\n",
|
|
device_get_nameunit(child));
|
|
return (ENXIO);
|
|
}
|
|
|
|
/* pcib interface functions */
|
|
|
|
static int
|
|
siba_pcib_maxslots(device_t dev)
|
|
{
|
|
|
|
return (SBPCI_SLOTMAX);
|
|
}
|
|
|
|
/*
|
|
* This needs hacking and fixery. It is currently broke and hangs.
|
|
* Debugging it will be tricky; there seems to be no way to enable
|
|
* a target abort which would cause a nice target abort.
|
|
* Look at linux again?
|
|
*/
|
|
static u_int32_t
|
|
siba_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
|
|
u_int reg, int bytes)
|
|
{
|
|
struct siba_pcib_softc *sc = device_get_softc(dev);
|
|
bus_addr_t cfgaddr;
|
|
uint32_t cfgtag;
|
|
uint32_t val;
|
|
|
|
/* XXX anything higher than slot 2 currently seems to hang the bus.
|
|
* not sure why this is; look at linux again
|
|
*/
|
|
if (bus != 0 || slot > 2) {
|
|
printf("%s: bad b/s/f %d/%d/%d\n", __func__, bus, slot, func);
|
|
return 0xffffffff; // XXX
|
|
}
|
|
|
|
device_printf(dev, "requested %d bytes from b/s/f %d/%d/%d reg %d\n",
|
|
bytes, bus, slot, func, reg);
|
|
|
|
/*
|
|
* The configuration tag on the broadcom is weird.
|
|
*/
|
|
SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI1, 2); /* XXX again??? */
|
|
cfgtag = ((1 << slot) << 16) | (func << 8);
|
|
cfgaddr = SBPCI_CFGBASE | cfgtag | (reg & ~3);
|
|
|
|
/* cfg space i/o is always 32 bits on this bridge */
|
|
printf("reading 4 bytes from %08x\n", cfgaddr);
|
|
val = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(cfgaddr); /* XXX MIPS */
|
|
|
|
val = bswap32(val); /* XXX seems to be needed for now */
|
|
|
|
/* swizzle and return what was asked for */
|
|
val &= 0xffffffff >> ((4 - bytes) * 8);
|
|
|
|
return (val);
|
|
}
|
|
|
|
static void
|
|
siba_pcib_write_config(device_t dev, u_int bus, u_int slot,
|
|
u_int func, u_int reg, u_int32_t val, int bytes)
|
|
{
|
|
|
|
/* write to pci configuration space */
|
|
//device_printf(dev, "%s: not yet implemented\n", __func__);
|
|
}
|
|
|
|
static int
|
|
siba_pcib_route_interrupt(device_t bridge, device_t device, int pin)
|
|
{
|
|
|
|
//device_printf(bridge, "%s: not yet implemented\n", __func__);
|
|
return (-1);
|
|
}
|
|
|
|
static device_method_t siba_pcib_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_attach, siba_pcib_attach),
|
|
DEVMETHOD(device_probe, siba_pcib_probe),
|
|
|
|
/* Bus interface */
|
|
DEVMETHOD(bus_print_child, bus_generic_print_child),
|
|
DEVMETHOD(bus_read_ivar, siba_pcib_read_ivar),
|
|
DEVMETHOD(bus_write_ivar, siba_pcib_write_ivar),
|
|
DEVMETHOD(bus_setup_intr, siba_pcib_setup_intr),
|
|
DEVMETHOD(bus_teardown_intr, siba_pcib_teardown_intr),
|
|
DEVMETHOD(bus_alloc_resource, siba_pcib_alloc_resource),
|
|
DEVMETHOD(bus_activate_resource, siba_pcib_activate_resource),
|
|
DEVMETHOD(bus_deactivate_resource, siba_pcib_deactivate_resource),
|
|
DEVMETHOD(bus_release_resource, siba_pcib_release_resource),
|
|
|
|
/* pcib interface */
|
|
DEVMETHOD(pcib_maxslots, siba_pcib_maxslots),
|
|
DEVMETHOD(pcib_read_config, siba_pcib_read_config),
|
|
DEVMETHOD(pcib_write_config, siba_pcib_write_config),
|
|
DEVMETHOD(pcib_route_interrupt, siba_pcib_route_interrupt),
|
|
|
|
KOBJMETHOD_END
|
|
};
|
|
|
|
static driver_t siba_pcib_driver = {
|
|
"pcib",
|
|
siba_pcib_methods,
|
|
sizeof(struct siba_softc),
|
|
};
|
|
static devclass_t siba_pcib_devclass;
|
|
|
|
DRIVER_MODULE(siba_pcib, siba, siba_pcib_driver, siba_pcib_devclass, 0, 0);
|