1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-23 11:18:54 +00:00

Add support for loadable microcode which implements interrupt coalescing

and packet bundling.  Make the microcode settings controllable via sysctl
and loader tunables.

Submitted by: Marko Zec <zec@tel.fer.hr>
  (with some munging and dynamic sysctl support by me)

Also extend the workaround for Dynamic Standby mode to later '559 chips,
not just the ICH2 variants.
This commit is contained in:
Jonathan Lemon 2001-10-25 05:32:01 +00:00
parent 9a7a8c90d1
commit 72a32a26aa
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=85462

View File

@ -39,6 +39,7 @@
/* #include <sys/mutex.h> */
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
@ -74,6 +75,7 @@
#include <dev/fxp/if_fxpreg.h>
#include <dev/fxp/if_fxpvar.h>
#include <dev/fxp/rcvbundl.h>
MODULE_DEPEND(fxp, miibus, 1, 1, 1);
#include "miibus_if.h"
@ -197,6 +199,11 @@ static void fxp_serial_ifmedia_sts(struct ifnet *ifp,
static volatile int fxp_miibus_readreg(device_t dev, int phy, int reg);
static void fxp_miibus_writereg(device_t dev, int phy, int reg,
int value);
static void fxp_load_ucode(struct fxp_softc *sc);
static int sysctl_int_range(SYSCTL_HANDLER_ARGS,
int low, int high);
static int sysctl_hw_fxp_bundle_max(SYSCTL_HANDLER_ARGS);
static int sysctl_hw_fxp_int_delay(SYSCTL_HANDLER_ARGS);
static __inline void fxp_lwcopy(volatile u_int32_t *src,
volatile u_int32_t *dst);
static __inline void fxp_scb_wait(struct fxp_softc *sc);
@ -470,18 +477,48 @@ fxp_attach(device_t dev)
sc->flags |= FXP_FLAG_SERIAL_MEDIA;
/*
* Find out the basic controller type; we currently only
* differentiate between a 82557 and greater.
* Create the sysctl tree
*/
sysctl_ctx_init(&sc->sysctl_ctx);
sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
device_get_nameunit(dev), CTLFLAG_RD, 0, "");
if (sc->sysctl_tree == NULL)
goto fail;
SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
OID_AUTO, "int_delay", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON,
&sc->tunable_int_delay, 0, &sysctl_hw_fxp_int_delay, "I",
"FXP driver receive interrupt microcode bundling delay");
SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
OID_AUTO, "bundle_max", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON,
&sc->tunable_bundle_max, 0, &sysctl_hw_fxp_bundle_max, "I",
"FXP driver receive interrupt microcode bundle size limit");
/*
* Pull in device tunables.
*/
sc->tunable_int_delay = TUNABLE_INT_DELAY;
sc->tunable_bundle_max = TUNABLE_BUNDLE_MAX;
(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
"int_delay", &sc->tunable_int_delay);
(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
"bundle_max", &sc->tunable_bundle_max);
/*
* Find out the chip revision; lump all 82557 revs together.
*/
fxp_read_eeprom(sc, &data, 5, 1);
if ((data >> 8) == 1)
sc->chip = FXP_CHIP_82557;
sc->revision = FXP_REV_82557;
else
sc->revision = pci_get_revid(dev);
/*
* Enable workarounds for certain chip revision deficiencies.
*
* Systems based on the ICH2/ICH2-M chip from Intel have a defect
* where the chip can cause a PCI protocol violation if it receives
* Systems based on the ICH2/ICH2-M chip from Intel, and possibly
* some systems based a normal 82559 design, have a defect where
* the chip can cause a PCI protocol violation if it receives
* a CU_RESUME command when it is entering the IDLE state. The
* workaround is to disable Dynamic Standby Mode, so the chip never
* deasserts CLKRUN#, and always remains in an active state.
@ -489,7 +526,8 @@ fxp_attach(device_t dev)
* See Intel 82801BA/82801BAM Specification Update, Errata #30.
*/
i = pci_get_device(dev);
if (i == 0x2449 || (i > 0x1030 && i < 0x1039)) {
if (i == 0x2449 || (i > 0x1030 && i < 0x1039) ||
sc->revision >= FXP_REV_82559_A0) {
fxp_read_eeprom(sc, &data, 10, 1);
if (data & 0x02) { /* STB enable */
u_int16_t cksum;
@ -534,7 +572,7 @@ fxp_attach(device_t dev)
/*
* If we are not a 82557 chip, we can enable extended features.
*/
if (sc->chip != FXP_CHIP_82557) {
if (sc->revision != FXP_REV_82557) {
/*
* If MWI is enabled in the PCI configuration, and there
* is a valid cacheline size (8 or 16 dwords), then tell
@ -563,7 +601,9 @@ fxp_attach(device_t dev)
pci_get_vendor(dev), pci_get_device(dev),
pci_get_subvendor(dev), pci_get_subdevice(dev),
pci_get_revid(dev));
device_printf(dev, "Chip Type: %d\n", sc->chip);
fxp_read_eeprom(sc, &data, 10, 1);
device_printf(dev, "Dynamic Standby mode is %s\n",
data & 0x02 ? "enabled" : "disabled");
}
/*
@ -655,6 +695,9 @@ fxp_release(struct fxp_softc *sc)
bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->irq);
if (sc->mem)
bus_release_resource(sc->dev, sc->rtp, sc->rgd, sc->mem);
sysctl_ctx_free(&sc->sysctl_ctx);
mtx_destroy(&sc->sc_mtx);
}
@ -1376,10 +1419,11 @@ fxp_stop(struct fxp_softc *sc)
untimeout(fxp_tick, sc, sc->stat_ch);
/*
* Issue software reset
* Issue software reset, which also unloads the microcode.
*/
CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
DELAY(10);
sc->flags &= ~FXP_FLAG_UCODE;
CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET);
DELAY(50);
/*
* Release any xmit buffers.
@ -1466,6 +1510,12 @@ fxp_init(void *xsc)
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats));
fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMP_ADR);
/*
* Attempt to load microcode if requested.
*/
if (ifp->if_flags & IFF_LINK0 && (sc->flags & FXP_FLAG_UCODE) == 0)
fxp_load_ucode(sc);
/*
* We temporarily use memory that contains the TxCB list to
* construct the config CB. The TxCB list memory is rebuilt
@ -1503,7 +1553,7 @@ fxp_init(void *xsc)
cbp->ext_txcb_dis = sc->flags & FXP_FLAG_EXT_TXCB ? 0 : 1;
cbp->ext_stats_dis = 1; /* disable extended counters */
cbp->keep_overrun_rx = 0; /* don't pass overrun frames to host */
cbp->save_bf = sc->chip == FXP_CHIP_82557 ? 1 : prm;
cbp->save_bf = sc->revision == FXP_REV_82557 ? 1 : prm;
cbp->disc_short_rx = !prm; /* discard short packets */
cbp->underrun_retry = 1; /* retry mode (once) on DMA underrun */
cbp->two_frames = 0; /* do not limit FIFO to 2 frames */
@ -1540,7 +1590,7 @@ fxp_init(void *xsc)
cbp->multi_ia = 0; /* (don't) accept multiple IAs */
cbp->mc_all = sc->flags & FXP_FLAG_ALL_MCAST ? 1 : 0;
if (sc->chip == FXP_CHIP_82557) {
if (sc->revision == FXP_REV_82557) {
/*
* The 82557 has no hardware flow control, the values
* below are the defaults for the chip.
@ -1933,8 +1983,8 @@ fxp_mc_setup(struct fxp_softc *sc)
sc->need_mcsetup = 1;
/*
* Add a NOP command with interrupt so that we are notified when all
* TX commands have been processed.
* Add a NOP command with interrupt so that we are notified
* when all TX commands have been processed.
*/
txp = sc->cbl_last->next;
txp->mb_head = NULL;
@ -2019,3 +2069,100 @@ fxp_mc_setup(struct fxp_softc *sc)
ifp->if_timer = 2;
return;
}
static u_int32_t fxp_ucode_d101a[] = D101_A_RCVBUNDLE_UCODE;
static u_int32_t fxp_ucode_d101b0[] = D101_B0_RCVBUNDLE_UCODE;
static u_int32_t fxp_ucode_d101ma[] = D101M_B_RCVBUNDLE_UCODE;
static u_int32_t fxp_ucode_d101s[] = D101S_RCVBUNDLE_UCODE;
static u_int32_t fxp_ucode_d102[] = D102_B_RCVBUNDLE_UCODE;
static u_int32_t fxp_ucode_d102c[] = D102_C_RCVBUNDLE_UCODE;
#define UCODE(x) x, sizeof(x)
struct ucode {
u_int32_t revision;
u_int32_t *ucode;
int length;
u_short int_delay_offset;
u_short bundle_max_offset;
} ucode_table[] = {
{ FXP_REV_82558_A4, UCODE(fxp_ucode_d101a), D101_CPUSAVER_DWORD, 0 },
{ FXP_REV_82558_B0, UCODE(fxp_ucode_d101b0), D101_CPUSAVER_DWORD, 0 },
{ FXP_REV_82559_A0, UCODE(fxp_ucode_d101ma),
D101M_CPUSAVER_DWORD, D101M_CPUSAVER_BUNDLE_MAX_DWORD },
{ FXP_REV_82559S_A, UCODE(fxp_ucode_d101s),
D101S_CPUSAVER_DWORD, D101S_CPUSAVER_BUNDLE_MAX_DWORD },
{ FXP_REV_82550, UCODE(fxp_ucode_d102),
D102_B_CPUSAVER_DWORD, D102_B_CPUSAVER_BUNDLE_MAX_DWORD },
{ FXP_REV_82550_C, UCODE(fxp_ucode_d102c),
D102_C_CPUSAVER_DWORD, D102_C_CPUSAVER_BUNDLE_MAX_DWORD },
{ 0, NULL, 0, 0, 0 }
};
static void
fxp_load_ucode(struct fxp_softc *sc)
{
struct ucode *uc;
struct fxp_cb_ucode *cbp;
for (uc = ucode_table; uc->ucode != NULL; uc++)
if (sc->revision == uc->revision)
break;
if (uc->ucode == NULL)
return;
cbp = (struct fxp_cb_ucode *)sc->cbl_base;
cbp->cb_status = 0;
cbp->cb_command = FXP_CB_COMMAND_UCODE | FXP_CB_COMMAND_EL;
cbp->link_addr = -1; /* (no) next command */
memcpy(cbp->ucode, uc->ucode, uc->length);
if (uc->int_delay_offset)
*(u_short *)&cbp->ucode[uc->int_delay_offset] =
sc->tunable_int_delay * 1.4881;
if (uc->bundle_max_offset)
*(u_short *)&cbp->ucode[uc->bundle_max_offset] =
sc->tunable_bundle_max;
/*
* Download the ucode to the chip.
*/
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status));
fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
/* ...and wait for it to complete. */
fxp_dma_wait(&cbp->cb_status, sc);
device_printf(sc->dev,
"Microcode loaded, int_delay: %d usec bundle_max: %d\n",
sc->tunable_int_delay,
uc->bundle_max_offset == 0 ? 0 : sc->tunable_bundle_max);
sc->flags |= FXP_FLAG_UCODE;
}
static int
sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
{
int error, value;
value = *(int *)arg1;
error = sysctl_handle_int(oidp, &value, 0, req);
if (error || !req->newptr)
return (error);
if (value < low || value > high)
return (EINVAL);
*(int *)arg1 = value;
return (0);
}
/*
* Interrupt delay is expressed in microseconds, a multiplier is used
* to convert this to the appropriate clock ticks before using.
*/
static int
sysctl_hw_fxp_int_delay(SYSCTL_HANDLER_ARGS)
{
return (sysctl_int_range(oidp, arg1, arg2, req, 300, 3000));
}
static int
sysctl_hw_fxp_bundle_max(SYSCTL_HANDLER_ARGS)
{
return (sysctl_int_range(oidp, arg1, arg2, req, 1, 0xffff));
}