/*-
* Copyright (c) 2011, 2012 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Radoslaw Kujawa.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
#ifdef PCI_NETBSD_CONFIGURE
/* Never reconfigure the bus on CVPPC/BVPPC, avoid the fb breakage. */
if (sc->bridge_type != P5PB_BRIDGE_CVPPC) {
p5pb_bus_reconfigure(sc);
}
#endif /* PCI_NETBSD_CONFIGURE */
/*
* Try to detect what kind of bridge are we dealing with.
*/
static bool
p5pb_identify_bridge(struct p5pb_softc *sc)
{
int pcires_count; /* Number of AutoConfig(TM) PCI resources */
pcires_count = p5pb_find_resources(sc);
switch (pcires_count) {
case 0:
/*
* Zero AutoConfig(TM) PCI resources, means that there's nothing
* OR there's a CVPPC/BVPPC with a pre-44.69 firmware.
*/
if (p5pb_cvppc_probe(sc)) {
sc->bridge_type = P5PB_BRIDGE_CVPPC;
aprint_normal(": Phase5 CVPPC/BVPPC PCI bridge\n");
} else {
aprint_normal(": no PCI bridges detected\n");
return false;
}
break;
case 6:
/*
* We have a slight possibility, that there's a CVPPC/BVPPC with
* the new firmware. So check for it first.
*/
if (p5pb_cvppc_probe(sc)) {
/* New firmware, treat as one-slot GREX. */
sc->bridge_type = P5PB_BRIDGE_CVPPC;
aprint_normal(
": Phase5 CVPPC/BVPPC PCI bridge (44.69/44.71)\n");
break;
}
default:
/* We have a G-REX surely. */
/*
* Set properties needed to support fb driver. These are read later during
* autoconfg in device_register(). Needed for CVPPC/BVPPC.
*/
void
p5pb_set_props(struct p5pb_softc *sc)
{
#if NGENFB > 0
prop_dictionary_t dict;
device_t dev;
dev = sc->sc_dev;
dict = device_properties(dev);
/* genfb needs additional properties, like virtual, physical address */
/* XXX: currently genfb is supported only on CVPPC/BVPPC */
prop_dictionary_set_uint64(dict, "virtual_address",
sc->pci_mem_area.base);
prop_dictionary_set_uint64(dict, "address",
kvtop((void*) sc->pci_mem_area.base));
#endif
}
int
p5pb_pci_conf_hook(pci_chipset_tag_t pct, int bus, int dev,
int func, pcireg_t id)
{
/* XXX: What should we do on CVPPC/BVPPC? It breaks genfb. */
return PCI_CONF_DEFAULT;
}
#ifdef P5PB_DEBUG
/* Check which config and I/O ranges are usable. */
void
p5pb_usable_ranges(struct p5pb_softc *sc)
{
p5pb_badaddr_range(sc, &(sc->pci_conf_area), 0, P5BUS_PCI_CONF_SIZE);
p5pb_badaddr_range(sc, &(sc->pci_io_area), 0, P5BUS_PCI_IO_SIZE);
}
void
p5pb_badaddr_range(struct p5pb_softc *sc, bus_space_tag_t bust, bus_addr_t base,
size_t len)
{
int i, state, prev_state;
bus_space_handle_t bush;
volatile void *data;
state = -1;
prev_state = -1;
bus_space_map(bust, base, len, 0, &bush);
aprint_normal("p5pb: badaddr range check from %x (%x) to %x (%x)\n",
(bus_addr_t) bush, /* start VA */
(bus_addr_t) kvtop((void*) bush), /* start PA */
(bus_addr_t) bush + len, /* end VA */
(bus_addr_t) kvtop((void*) (bush + len)));/* end PA */
data = bus_space_vaddr(bust, bush);
for(i = 0; i < len; i++) {
state = badaddr((void *)__UNVOLATILE(((uint32_t) data + i)));
if(state != prev_state) {
aprint_normal("p5pb: badaddr %p (%x) : %d\n",
(void*) ((uint32_t) data + i),
(bus_addr_t) kvtop((void*) ((uint32_t) data + i)),
state);
prev_state = state;
}
}
bus_space_unmap(bust, bush, len);
}
/* Search for 16-bit value in the configuration space. */
void
p5pb_conf_search(struct p5pb_softc *sc, uint16_t val)
{
int i, state;
uint16_t readv;
void *va;
va = bus_space_vaddr(sc->apc.pci_conf_datat, sc->apc.pci_conf_datah);
for (i = 0; i < P5BUS_PCI_CONF_SIZE; i++) {
state = badaddr((void *)__UNVOLATILE(((uint32_t) va + i)));
if(state == 0) {
readv = bus_space_read_2(sc->apc.pci_conf_datat,
sc->apc.pci_conf_datah, i);
if(readv == val)
aprint_normal("p5pb: found val %x @ %x (%x)\n",
readv, (uint32_t) sc->apc.pci_conf_datah
+ i, (bus_addr_t) kvtop((void*)
((uint32_t) sc->apc.pci_conf_datah + i)));
}
}
}