Improve Cardbus performance, error handling, and error reporting.

Let the TI1420 PCI-Cardbus bridge do burst reads from the primary
(PCI) bus.  This ought to improve Tx performance on Cardbus NICs.
This optimization may apply to other TI bridges, but I only have
a datasheet for the TI1420. :-/

Activate PCI Parity Error and System Error reporting on PCI-Cardbus
bridges.

To avoid data destruction, set the Master Abort mode to 1.  Stops
the bridge from silently discarding writes from the secondary bus
to the primary bus (Cardbus writes to PCI).  Also, stops the bridge
from fulfilling a read by a bus master on the secondary bus that
failed on the primary bus with 0xffffffff (Cardbus reads from PCI).
Now the bus will indicate an error condition (SERR) instead of
silently destroying/corrupting data.

Forward system error indications from the secondary to the primary
bus.  Detect parity errors on the secondary.

Set a Cardbus card's Latency Timer to something reasonable, according
to the Cardbus card's Minimum Grant and the bandwidth available on
the PCI bus.  Restore the Latency Timer when re-enabling a card
(e.g., after power reactivation).
This commit is contained in:
dyoung 2007-11-16 18:36:51 +00:00
parent 3537e99eb1
commit 43d2148e33
4 changed files with 109 additions and 34 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: cardbus.c,v 1.76 2007/10/19 11:59:38 ad Exp $ */
/* $NetBSD: cardbus.c,v 1.77 2007/11/16 18:36:52 dyoung Exp $ */
/*
* Copyright (c) 1997, 1998, 1999 and 2000
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cardbus.c,v 1.76 2007/10/19 11:59:38 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: cardbus.c,v 1.77 2007/11/16 18:36:52 dyoung Exp $");
#include "opt_cardbus.h"
@ -122,12 +122,12 @@ cardbusattach(struct device *parent, struct device *self, void *aux)
sc->sc_bus = cba->cba_bus;
sc->sc_intrline = cba->cba_intrline;
sc->sc_cacheline = cba->cba_cacheline;
sc->sc_lattimer = cba->cba_lattimer;
sc->sc_max_lattimer = MIN(0xf8, cba->cba_max_lattimer);
printf(": bus %d", sc->sc_bus);
if (bootverbose)
printf(" cacheline 0x%x, lattimer 0x%x", sc->sc_cacheline,
sc->sc_lattimer);
sc->sc_max_lattimer);
printf("\n");
sc->sc_iot = cba->cba_iot; /* CardBus I/O space tag */
@ -194,7 +194,7 @@ cardbus_read_tuples(struct cardbus_attach_args *ca, cardbusreg_t cis_ptr,
DPRINTF(("%s: reading CIS data from ROM\n",
sc->sc_dev.dv_xname));
} else {
reg = CARDBUS_BASE0_REG + (cardbus_space - 1) * 4;
reg = CARDBUS_CIS_ASI_BAR(cardbus_space);
DPRINTF(("%s: reading CIS data from BAR%d\n",
sc->sc_dev.dv_xname, cardbus_space - 1));
}
@ -258,8 +258,10 @@ cardbus_read_tuples(struct cardbus_attach_args *ca, cardbusreg_t cis_ptr,
CARDBUS_COMMAND_STATUS_REG,
command | CARDBUS_COMMAND_MEM_ENABLE);
/* XXX byte order? */
printf("%s: ding!\n", __func__);
bus_space_read_region_1(ca->ca_memt, bar_memh,
cis_ptr, tuples, MIN(bar_size, len));
printf("%s: dong!\n", __func__);
found++;
}
command = cardbus_conf_read(cc, cf, tag,
@ -415,7 +417,7 @@ cardbus_rescan(struct device *self, const char *ifattr,
cardbus_function_tag_t cf;
cardbustag_t tag;
cardbusreg_t id, class, cis_ptr;
cardbusreg_t bhlc;
cardbusreg_t bhlc, icr, lattimer;
int cdstatus;
int function, nfunction;
struct device *csc;
@ -442,6 +444,9 @@ cardbus_rescan(struct device *self, const char *ifattr,
/*
* Wait until power comes up. Maxmum 500 ms.
*
* XXX What is this for? The bridge driver ought to have waited
* XXX already.
*/
{
int i;
@ -509,27 +514,39 @@ cardbus_rescan(struct device *self, const char *ifattr,
/* set initial latency and cacheline size */
bhlc = cardbus_conf_read(cc, cf, tag, CARDBUS_BHLC_REG);
DPRINTF(("%s func%d bhlc 0x%08x -> ", sc->sc_dev.dv_xname,
function, bhlc));
bhlc &= ~((CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT) |
(CARDBUS_CACHELINE_MASK << CARDBUS_CACHELINE_SHIFT));
icr = cardbus_conf_read(cc, cf, tag, CARDBUS_INTERRUPT_REG);
DPRINTF(("%s func%d icr 0x%08x bhlc 0x%08x -> ",
device_xname(&sc->sc_dev), function, icr, bhlc));
bhlc &= ~(CARDBUS_CACHELINE_MASK << CARDBUS_CACHELINE_SHIFT);
bhlc |= (sc->sc_cacheline & CARDBUS_CACHELINE_MASK) <<
CARDBUS_CACHELINE_SHIFT;
bhlc |= (sc->sc_lattimer & CARDBUS_LATTIMER_MASK) <<
CARDBUS_LATTIMER_SHIFT;
/*
* Set the initial value of the Latency Timer.
*
* While a PCI device owns the bus, its Latency
* Timer counts down bus cycles from its initial
* value to 0. Minimum Grant tells for how long
* the device wants to own the bus once it gets
* access, in units of 250ns.
*
* On a 33 MHz bus, there are 8 cycles per 250ns.
* So I multiply the Minimum Grant by 8 to find
* out the initial value of the Latency Timer.
*
* Avoid setting a Latency Timer less than 0x10,
* since the old code did not do that.
*/
lattimer =
MIN(sc->sc_max_lattimer, MAX(0x10, 8 * PCI_MIN_GNT(icr)));
if (PCI_LATTIMER(bhlc) < lattimer) {
bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
bhlc |= (lattimer << PCI_LATTIMER_SHIFT);
}
cardbus_conf_write(cc, cf, tag, CARDBUS_BHLC_REG, bhlc);
bhlc = cardbus_conf_read(cc, cf, tag, CARDBUS_BHLC_REG);
DPRINTF(("0x%08x\n", bhlc));
if (CARDBUS_LATTIMER(bhlc) < 0x10) {
bhlc &= ~(CARDBUS_LATTIMER_MASK <<
CARDBUS_LATTIMER_SHIFT);
bhlc |= (0x10 << CARDBUS_LATTIMER_SHIFT);
cardbus_conf_write(cc, cf, tag,
CARDBUS_BHLC_REG, bhlc);
}
/*
* We need to allocate the ct here, since we might
* need it when reading the CIS
@ -539,6 +556,7 @@ cardbus_rescan(struct device *self, const char *ifattr,
panic("no room for cardbus_tag");
}
ct->ct_bhlc = bhlc;
ct->ct_cc = sc->sc_cc;
ct->ct_cf = sc->sc_cf;
ct->ct_bus = sc->sc_bus;
@ -763,6 +781,7 @@ cardbus_function_enable(struct cardbus_softc *sc, int func)
{
cardbus_chipset_tag_t cc = sc->sc_cc;
cardbus_function_tag_t cf = sc->sc_cf;
cardbus_devfunc_t ct;
cardbusreg_t command;
cardbustag_t tag;
@ -783,6 +802,9 @@ cardbus_function_enable(struct cardbus_softc *sc, int func)
cardbus_conf_write(cc, cf, tag, CARDBUS_COMMAND_STATUS_REG, command);
if ((ct = sc->sc_funcs[func]) != NULL)
Cardbus_conf_write(ct, tag, CARDBUS_BHLC_REG, ct->ct_bhlc);
cardbus_free_tag(cc, cf, tag);
DPRINTF(("%x\n", sc->sc_poweron_func));

View File

@ -1,4 +1,4 @@
/* $NetBSD: cardbusvar.h,v 1.35 2005/12/11 12:21:15 christos Exp $ */
/* $NetBSD: cardbusvar.h,v 1.36 2007/11/16 18:36:53 dyoung Exp $ */
/*
* Copyright (c) 1998, 1999 and 2000
@ -127,7 +127,7 @@ typedef struct cardbus_functions {
#endif /* rbus */
/*
* struct cbslot_attach_args is the attach argument for cardbus card.
* struct cbslot_attach_args is the attach argument for Cardbus cardslot.
*/
struct cbslot_attach_args {
const char *cba_busname;
@ -147,7 +147,17 @@ struct cbslot_attach_args {
#endif
int cba_cacheline; /* cache line size */
int cba_lattimer; /* latency timer */
int cba_max_lattimer; /* No card's latency timer may
* exceed this. On a PCI-Cardbus
* bridge, I let the latency timer on
* the primary bus (PCI bus) set
* the maximum. That is kind of an
* arbitrary choice. A more sensible
* choice may be either the
* latency timer on the primary bus,
* or the bridge's buffer size,
* whichever is larger.
*/
};
@ -175,7 +185,12 @@ struct cardbus_softc {
#endif
int sc_cacheline; /* cache line size */
int sc_lattimer; /* latency timer */
int sc_max_lattimer; /* No card's latency timer
* may exceed this. On a PCI-Cardbus
* bridge, the latency timer on
* the primary bus (PCI bus) sets
* the maximum.
*/
int sc_volt; /* applied Vcc voltage */
#define PCCARD_33V 0x02
#define PCCARD_XXV 0x04
@ -204,8 +219,8 @@ typedef struct cardbus_devfunc {
rbus_tag_t ct_rbus_memt; /* CardBus mem rbus tag */
#endif
u_int32_t ct_bar[6]; /* Base Address Regs 0 to 6 */
u_int32_t ct_lc; /* Latency timer and cache line size */
cardbusreg_t ct_bar[6]; /* Base Address Regs 0 to 6 */
cardbusreg_t ct_bhlc; /* Latency timer and cache line size */
/* u_int32_t ct_cisreg; */ /* CIS reg: is it needed??? */
struct device *ct_device; /* pointer to the device */

View File

@ -1,4 +1,4 @@
/* $NetBSD: pccbb.c,v 1.150 2007/10/25 13:49:06 joerg Exp $ */
/* $NetBSD: pccbb.c,v 1.151 2007/11/16 18:36:51 dyoung Exp $ */
/*
* Copyright (c) 1998, 1999 and 2000
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pccbb.c,v 1.150 2007/10/25 13:49:06 joerg Exp $");
__KERNEL_RCSID(0, "$NetBSD: pccbb.c,v 1.151 2007/11/16 18:36:51 dyoung Exp $");
/*
#define CBB_DEBUG
@ -93,6 +93,8 @@ struct cfdriver cbb_cd = {
#define STATIC static
#endif
int pccbb_burstup = 1;
/*
* delay_ms() is wait in milliseconds. It should be used instead
* of delay() if you want to wait more than 1 ms.
@ -312,7 +314,7 @@ const struct yenta_chipinfo {
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1410), CB_TI12XX,
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1420), CB_TI12XX,
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1420), CB_TI1420,
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
{ MAKEID(PCI_VENDOR_TI, PCI_PRODUCT_TI_PCI1450), CB_TI125X,
PCCBB_PCMCIA_IO_RELOC | PCCBB_PCMCIA_MEM_32},
@ -683,12 +685,12 @@ pccbb_pci_callback(struct device *self)
#endif
cba.cba_cacheline = PCI_CACHELINE(bhlc);
cba.cba_lattimer = PCI_LATTIMER(bhlc);
cba.cba_max_lattimer = PCI_LATTIMER(bhlc);
if (bootverbose) {
printf("%s: cacheline 0x%x lattimer 0x%x\n",
sc->sc_dev.dv_xname, cba.cba_cacheline,
cba.cba_lattimer);
cba.cba_max_lattimer);
printf("%s: bhlc 0x%x\n",
device_xname(&sc->sc_dev), bhlc);
}
@ -738,8 +740,8 @@ pccbb_chipinit(struct pccbb_softc *sc)
pcitag_t tag = sc->sc_tag;
bus_space_tag_t bmt = sc->sc_base_memt;
bus_space_handle_t bmh = sc->sc_base_memh;
pcireg_t bcr, bhlc, cbctl, csr, lscp, mfunc, slotctl, sockctl, sockmask,
sysctrl;
pcireg_t bcr, bhlc, cbctl, csr, lscp, mfunc, mrburst, slotctl, sockctl,
sockmask, sysctrl;
/*
* Set PCI command reg.
@ -749,6 +751,7 @@ pccbb_chipinit(struct pccbb_softc *sc)
/* I believe it is harmless. */
csr |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
PCI_COMMAND_MASTER_ENABLE);
csr |= (PCI_COMMAND_PARITY_ENABLE|PCI_COMMAND_SERR_ENABLE);
pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
/*
@ -782,6 +785,12 @@ pccbb_chipinit(struct pccbb_softc *sc)
bcr |= CB_BCR_WRITE_POST_ENABLE; /* enable write post */
/* assert reset */
bcr |= PCI_BRIDGE_CONTROL_SECBR << PCI_BRIDGE_CONTROL_SHIFT;
/* Set master abort mode to 1, forward SERR# from secondary
* to primary, and detect parity errors on secondary.
*/
bcr |= PCI_BRIDGE_CONTROL_MABRT << PCI_BRIDGE_CONTROL_SHIFT;
bcr |= PCI_BRIDGE_CONTROL_SERR << PCI_BRIDGE_CONTROL_SHIFT;
bcr |= PCI_BRIDGE_CONTROL_PERE << PCI_BRIDGE_CONTROL_SHIFT;
pci_conf_write(pc, tag, PCI_BRIDGE_CONTROL_REG, bcr);
switch (sc->sc_chipset) {
@ -797,6 +806,28 @@ pccbb_chipinit(struct pccbb_softc *sc)
pci_conf_write(pc, tag, PCI_CBCTRL, cbctl);
break;
case CB_TI1420:
sysctrl = pci_conf_read(pc, tag, PCI_SYSCTRL);
mrburst = pccbb_burstup
? PCI1420_SYSCTRL_MRBURST : PCI1420_SYSCTRL_MRBURSTDN;
if ((sysctrl & PCI1420_SYSCTRL_MRBURST) == mrburst) {
printf("%s: %swrite bursts enabled\n",
device_xname(&sc->sc_dev),
pccbb_burstup ? "read/" : "");
} else if (pccbb_burstup) {
printf("%s: enabling read/write bursts\n",
device_xname(&sc->sc_dev));
sysctrl |= PCI1420_SYSCTRL_MRBURST;
pci_conf_write(pc, tag, PCI_SYSCTRL, sysctrl);
} else {
printf("%s: disabling read bursts, "
"enabling write bursts\n",
device_xname(&sc->sc_dev));
sysctrl |= PCI1420_SYSCTRL_MRBURSTDN;
sysctrl &= ~PCI1420_SYSCTRL_MRBURSTUP;
pci_conf_write(pc, tag, PCI_SYSCTRL, sysctrl);
}
/*FALLTHROUGH*/
case CB_TI12XX:
/*
* Some TI 12xx (and [14][45]xx) based pci cards

View File

@ -1,4 +1,4 @@
/* $NetBSD: pccbbreg.h,v 1.12 2007/08/11 00:31:05 dyoung Exp $ */
/* $NetBSD: pccbbreg.h,v 1.13 2007/11/16 18:36:52 dyoung Exp $ */
/*
* Copyright (c) 1999 HAYAKAWA Koichi. All rights reserved.
*
@ -131,6 +131,13 @@
#define PCI12XX_CBCTRL_SPK_ENA 0x0200 /* Speaker enable */
#define PCI12XX_CBCTRL_INTR_DET 0x0100 /* functional interrupt detect */
/* 1: permit burst read from CardBus (default: on) */
#define PCI1420_SYSCTRL_MRBURSTDN __BIT(15)
/* 1: permit burst read from PCI bus (default: off!) */
#define PCI1420_SYSCTRL_MRBURSTUP __BIT(14)
#define PCI1420_SYSCTRL_MRBURST \
(PCI1420_SYSCTRL_MRBURSTDN|PCI1420_SYSCTRL_MRBURSTUP)
/* PCI_BCR_INTR additional bit for Rx5C46[567] */
#define CB_BCRI_RL_3E0_ENA 0x08000000