/*
* Copyright (c) 1996, 1998 Christopher G. Demetriou. 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christopher G. Demetriou
* for the NetBSD Project.
* 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.
*/
/*
* Check the ID register to see that it's a PCI bridge.
* If it is, we assume that we can deal with it; it _should_
* work in a standardized way...
*/
if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_PCI)
return 1;
#ifdef _MIPS_PADDR_T_64BIT
/* The LDT HB acts just like a PPB. */
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SIBYTE
&& PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SIBYTE_BCM1250_LDTHB)
return 1;
#endif
if (!pci_get_capability(sc->sc_pc, sc->sc_tag, PCI_CAP_PCIEXPRESS,
&off, ®))
return; /* Not a PCIe device */
capversion = PCIE_XCAP_VER(reg);
devtype = PCIE_XCAP_TYPE(reg);
aprint_normal_dev(self, "PCI Express capability version ");
switch (capversion) {
case PCIE_XCAP_VER_1:
aprint_normal("1");
break;
case PCIE_XCAP_VER_2:
aprint_normal("2");
break;
default:
aprint_normal_dev(self, "unsupported (%d)\n", capversion);
return;
}
aprint_normal(" <");
switch (devtype) {
case PCIE_XCAP_TYPE_PCIE_DEV:
aprint_normal("PCI-E Endpoint device");
break;
case PCIE_XCAP_TYPE_PCI_DEV:
aprint_normal("Legacy PCI-E Endpoint device");
break;
case PCIE_XCAP_TYPE_RP:
aprint_normal("Root Port of PCI-E Root Complex");
break;
case PCIE_XCAP_TYPE_UP:
aprint_normal("Upstream Port of PCI-E Switch");
break;
case PCIE_XCAP_TYPE_DOWN:
aprint_normal("Downstream Port of PCI-E Switch");
break;
case PCIE_XCAP_TYPE_PCIE2PCI:
aprint_normal("PCI-E to PCI/PCI-X Bridge");
break;
case PCIE_XCAP_TYPE_PCI2PCIE:
aprint_normal("PCI/PCI-X to PCI-E Bridge");
break;
default:
aprint_normal("Device/Port Type %x", devtype);
break;
}
switch (devtype) {
case PCIE_XCAP_TYPE_RP:
case PCIE_XCAP_TYPE_DOWN:
case PCIE_XCAP_TYPE_PCI2PCIE:
reg = pci_conf_read(sc->sc_pc, sc->sc_tag, off + PCIE_LCAP);
u_int mlw = __SHIFTOUT(reg, PCIE_LCAP_MAX_WIDTH);
u_int mls = __SHIFTOUT(reg, PCIE_LCAP_MAX_SPEED);
if (PCI_BRIDGE_BUS_NUM_SECONDARY(busdata) == 0) {
aprint_normal_dev(self, "not configured by system firmware\n");
return;
}
ppb_print_pcie(self);
#if 0
/*
* XXX can't do this, because we're not given our bus number
* (we shouldn't need it), and because we've no way to
* decompose our tag.
*/
/* sanity check. */
if (pa->pa_bus != PCI_BRIDGE_BUS_NUM_PRIMARY(busdata))
panic("ppbattach: bus in tag (%d) != bus in reg (%d)",
pa->pa_bus, PCI_BRIDGE_BUS_NUM_PRIMARY(busdata));
#endif
/* Enable interrupt. */
val = 0;
slcap = pci_conf_read(pc, pa->pa_tag,
sc->sc_pciecapoff + PCIE_SLCAP);
if (slcap & PCIE_SLCAP_ABP)
val |= PCIE_SLCSR_ABE;
if (slcap & PCIE_SLCAP_PCP)
val |= PCIE_SLCSR_PFE;
if (slcap & PCIE_SLCAP_MSP)
val |= PCIE_SLCSR_MSE;
#if 0
/*
* XXX Disable for a while because setting
* PCIE_SLCSR_CCE makes break device access on
* some environment.
*/
if ((slcap & PCIE_SLCAP_NCCS) == 0)
val |= PCIE_SLCSR_CCE;
#endif
/* Attention indicator off by default */
if (slcap & PCIE_SLCAP_AIP) {
val |= __SHIFTIN(PCIE_SLCSR_IND_OFF,
PCIE_SLCSR_AIC);
}
/* Power indicator */
if (slcap & PCIE_SLCAP_PIP) {
/*
* Indicator off:
* a) card not present
* b) power fault
* c) MRL sensor off
*/
if (((slcsr & PCIE_SLCSR_PDS) == 0)
|| ((slcsr & PCIE_SLCSR_PFD) != 0)
|| (((slcap & PCIE_SLCAP_MSP) != 0)
&& ((slcsr & PCIE_SLCSR_MS) != 0)))
val |= __SHIFTIN(PCIE_SLCSR_IND_OFF,
PCIE_SLCSR_PIC);
else
val |= __SHIFTIN(PCIE_SLCSR_IND_ON,
PCIE_SLCSR_PIC);
}
configure:
/*
* If the secondary bus is configured and the bus mastering is not
* enabled, enable it.
*/
if (second_configured) {
reg = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
if ((reg & PCI_COMMAND_MASTER_ENABLE) == 0)
pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
reg | PCI_COMMAND_MASTER_ENABLE);
}
if (!pmf_device_register(self, ppb_suspend, ppb_resume))
aprint_error_dev(self, "couldn't establish power handler\n");
/*
* Attach the PCI bus that hangs off of it.
*
* XXX Don't pass-through Memory Read Multiple. Should we?
* XXX Consult the spec...
*/
pba.pba_iot = pa->pa_iot;
pba.pba_memt = pa->pa_memt;
pba.pba_dmat = pa->pa_dmat;
pba.pba_dmat64 = pa->pa_dmat64;
pba.pba_pc = pc;
pba.pba_flags = pa->pa_flags & ~PCI_FLAGS_MRM_OKAY;
pba.pba_bus = PCI_BRIDGE_BUS_NUM_SECONDARY(busdata);
pba.pba_sub = PCI_BRIDGE_BUS_NUM_SUBORDINATE(busdata);
pba.pba_bridgetag = &sc->sc_tag;
pba.pba_intrswiz = pa->pa_intrswiz;
pba.pba_intrtag = pa->pa_intrtag;
config_found(self, &pba, pcibusprint,
/*
* Forward along the device handle for the bridge to the
* downstream bus.
*/
CFARGS(.devhandle = device_handle(self)));
}
static int
ppbdetach(device_t self, int flags)
{
#ifdef PPB_USEINTR
struct ppb_softc *sc = device_private(self);
pcireg_t slcsr;
#endif
int rc;
if ((rc = config_detach_children(self, flags)) != 0)
return rc;
/*
* Not me. This check is only required for INTx.
* ppb_intr() would be split into ppb_intr_legacy() and ppb_intr_msi()
*/
if ((reg & PCIE_SLCSR_STATCHG_MASK) == 0)
return 0;
/* Command Completed */
if (reg & PCIE_SLCSR_CC) {
sc->sc_ev_cc.ev_count++;
if (ppb_printevent)
device_printf(dev, "Command Completed\n");
}
/* Data Link Layer State Changed */
if (reg & PCIE_SLCSR_LACS) {
sc->sc_ev_lacs.ev_count++;
if (ppb_printevent)
device_printf(dev, "Data Link Layer State Changed\n");
}