summaryrefslogtreecommitdiffstats
path: root/sys/dev/fxp
diff options
context:
space:
mode:
authorjlemon <jlemon@FreeBSD.org>2001-10-25 05:32:01 +0000
committerjlemon <jlemon@FreeBSD.org>2001-10-25 05:32:01 +0000
commit67beb4eaf7faa5b25d3bb9dce795b33b1db0a037 (patch)
treeff7f8b0d656be9a58d9b7751453fbee54c4c833b /sys/dev/fxp
parent4436456da7f2295f9f3b31d390dfe6c0138ddc8b (diff)
downloadFreeBSD-src-67beb4eaf7faa5b25d3bb9dce795b33b1db0a037.zip
FreeBSD-src-67beb4eaf7faa5b25d3bb9dce795b33b1db0a037.tar.gz
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.
Diffstat (limited to 'sys/dev/fxp')
-rw-r--r--sys/dev/fxp/if_fxp.c177
1 files changed, 162 insertions, 15 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index 3f8f2aa..2d0837f 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -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.
@@ -1467,6 +1511,12 @@ fxp_init(void *xsc)
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
* later.
@@ -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));
+}
OpenPOWER on IntegriCloud