From 72a32a26aa76fde45219333af91aa8bafae70dc0 Mon Sep 17 00:00:00 2001 From: Jonathan Lemon Date: Thu, 25 Oct 2001 05:32:01 +0000 Subject: [PATCH] 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 (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. --- sys/dev/fxp/if_fxp.c | 177 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 162 insertions(+), 15 deletions(-) diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c index 3f8f2aac6c2f..2d0837f6e439 100644 --- a/sys/dev/fxp/if_fxp.c +++ b/sys/dev/fxp/if_fxp.c @@ -39,6 +39,7 @@ /* #include */ #include #include +#include #include #include @@ -74,6 +75,7 @@ #include #include +#include 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)); +}