diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2006-03-24 03:08:01 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2006-03-24 03:08:01 +0000 |
commit | 89a5428c5f27e32c988f2c6344ee0567ca58a51a (patch) | |
tree | a5d91b5692511315a66c723c3fea2a4c2bfcc628 | |
parent | 2b0fe17b55eb586b4df03c2614e459bef8e22ee2 (diff) |
- Set the PCI latency timer for bus master devices.
- Turn on parity checking for the PCI bus.
From NetBSD
Tested on U5/U10/U30/Netra X1/T1 105/Leopard-V
-rw-r--r-- | sys/arch/sparc64/dev/pci_machdep.c | 90 |
1 files changed, 87 insertions, 3 deletions
diff --git a/sys/arch/sparc64/dev/pci_machdep.c b/sys/arch/sparc64/dev/pci_machdep.c index 2582d98aefc..971a2102b51 100644 --- a/sys/arch/sparc64/dev/pci_machdep.c +++ b/sys/arch/sparc64/dev/pci_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pci_machdep.c,v 1.22 2006/03/19 02:43:38 brad Exp $ */ +/* $OpenBSD: pci_machdep.c,v 1.23 2006/03/24 03:08:00 brad Exp $ */ /* $NetBSD: pci_machdep.c,v 1.22 2001/07/20 00:07:13 eeh Exp $ */ /* @@ -56,7 +56,6 @@ int sparc_pci_debug = 0x0; #include <machine/bus.h> #include <machine/autoconf.h> #include <machine/openfirm.h> - #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> @@ -66,12 +65,15 @@ int sparc_pci_debug = 0x0; #include <sparc64/dev/iommuvar.h> #include <sparc64/dev/psychoreg.h> #include <sparc64/dev/psychovar.h> +#include <sparc64/sparc64/cache.h> /* this is a base to be copied */ struct sparc_pci_chipset _sparc_pci_chipset = { NULL, }; +static int pci_bus_frequency(int node); + static pcitag_t ofpci_make_tag(pci_chipset_tag_t pc, int node, int b, int d, int f) { @@ -242,6 +244,27 @@ pci_decompose_tag(pc, tag, bp, dp, fp) *fp = PCITAG_FUN(tag); } +static int +pci_bus_frequency(int node) +{ + int len, bus_frequency; + + len = OF_getproplen(node, "clock-frequency"); + if (len < sizeof(bus_frequency)) { + DPRINTF(SPDB_PROBE, + ("pci_bus_frequency: clock-frequency len %d too small\n", + len)); + return 33; + } + if (OF_getprop(node, "clock-frequency", &bus_frequency, + sizeof(bus_frequency)) != len) { + DPRINTF(SPDB_PROBE, + ("pci_bus_frequency: could not read clock-frequency\n")); + return 33; + } + return bus_frequency / 1000000; +} + int sparc64_pci_enumerate_bus(struct pci_softc *sc, int (*match)(struct pci_attach_args *), struct pci_attach_args *pap) @@ -249,8 +272,9 @@ sparc64_pci_enumerate_bus(struct pci_softc *sc, struct ofw_pci_register reg; pci_chipset_tag_t pc = sc->sc_pc; pcitag_t tag; - pcireg_t class; + pcireg_t class, csr, bhlc, ic; int node, b, d, f, ret; + int bus_frequency, lt, cl, cacheline; char name[30]; if (sc->sc_bridgetag) @@ -258,6 +282,32 @@ sparc64_pci_enumerate_bus(struct pci_softc *sc, else node = pc->rootnode; + bus_frequency = pci_bus_frequency(node); + + /* + * Make sure the cache line size is at least as big as the + * ecache line and the streaming cache (64 byte). + */ + cacheline = max(cacheinfo.ec_linesize, 64); + KASSERT((cacheline/64)*64 == cacheline && + (cacheline/cacheinfo.ec_linesize)*cacheinfo.ec_linesize == cacheline && + (cacheline/4)*4 == cacheline); + + /* Turn on parity for the bus. */ + tag = ofpci_make_tag(pc, node, sc->sc_bus, 0, 0); + csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + csr |= PCI_COMMAND_PARITY_ENABLE; + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); + + /* + * Initialize the latency timer register. + * The value 0x40 is from Solaris. + */ + bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG); + bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); + bhlc |= 0x40 << PCI_LATTIMER_SHIFT; + pci_conf_write(pc, tag, PCI_BHLC_REG, bhlc); + for (node = OF_child(node); node != 0 && node != -1; node = OF_peer(node)) { name[0] = name[29] = 0; @@ -280,6 +330,40 @@ sparc64_pci_enumerate_bus(struct pci_softc *sc, } tag = ofpci_make_tag(pc, node, b, d, f); + + /* + * Turn on parity and fast-back-to-back for the device. + */ + csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); + if (csr & PCI_STATUS_BACKTOBACK_SUPPORT) + csr |= PCI_COMMAND_BACKTOBACK_ENABLE; + csr |= PCI_COMMAND_PARITY_ENABLE; + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); + + /* + * Initialize the latency timer register for busmaster + * devices to work properly. + * latency-timer = min-grant * bus-freq / 4 (from FreeBSD) + * Also initialize the cache line size register. + * Solaris anytime sets this register to the value 0x10. + */ + bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG); + ic = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); + + lt = min(PCI_MIN_GNT(ic) * bus_frequency / 4, 255); + if (lt == 0 || lt < PCI_LATTIMER(bhlc)) + lt = PCI_LATTIMER(bhlc); + + cl = PCI_CACHELINE(bhlc); + if (cl == 0) + cl = cacheline; + + bhlc &= ~((PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT) | + (PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT)); + bhlc |= (lt << PCI_LATTIMER_SHIFT) | + (cl << PCI_CACHELINE_SHIFT); + pci_conf_write(pc, tag, PCI_BHLC_REG, bhlc); + ret = pci_probe_device(sc, tag, match, pap); if (match != NULL && ret != 0) return (ret); |