Code review
M. Warner Losh
imp at bsdimp.com
Mon Aug 25 06:26:18 UTC 2008
I did this a few years ago when trying to track down a problem with
some realtek network chips that I was having problems with at Timing
Solutions. I'd like to get this into the tree, since it was helpful
then.
Comments?
Warner
-------------- next part --------------
diff -ur src/sys/pci/if_rl.c newcard/src/sys/pci/if_rl.c
--- src/sys/pci/if_rl.c 2008-08-23 22:21:15.000000000 -0600
+++ newcard/src/sys/pci/if_rl.c 2008-08-23 22:26:09.000000000 -0600
@@ -1253,18 +1253,120 @@
}
static void
+rl_twister_update(struct rl_softc *sc)
+{
+ uint16_t linktest;
+ static const uint32_t param[4][4] = {
+ {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+ };
+
+ /*
+ * Tune the so-called twister registers of the RTL8139. These
+ * are used to compensate for impendence mismatches. The
+ * method for tuning these registes is undocumented and the
+ * following proceedure is collected from public sources.
+ */
+ switch (sc->rl_twister)
+ {
+ case CHK_LINK:
+ /*
+ * If we have a sufficent link, then we can proceed in
+ * the state machine to the next stage. If not, then
+ * disable further tuning after writing sane defaults.
+ */
+ if (CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_LINK_OK) {
+ CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_OFF_CMD);
+ sc->rl_twister = FIND_ROW;
+ } else {
+ CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_CMD);
+ CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST);
+ CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF);
+ CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF);
+ sc->rl_twister = DONE;
+ }
+ break;
+ case FIND_ROW:
+ /*
+ * Read how long it took to see the echo to find the tuning
+ * row to use.
+ */
+ linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS;
+ if (linktest == RL_CSCFG_ROW3)
+ sc->rl_twist_row = 3;
+ else if (linktest == RL_CSCFG_ROW2)
+ sc->rl_twist_row = 2;
+ else if (linktest == RL_CSCFG_ROW1)
+ sc->rl_twist_row = 1;
+ else
+ sc->rl_twist_row = 0;
+ sc->rl_twist_col = 0;
+ sc->rl_twister = SET_PARAM;
+ break;
+ case SET_PARAM:
+ if (sc->rl_twist_col == 0)
+ CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET);
+ CSR_WRITE_4(sc, RL_PARA7C,
+ param[sc->rl_twist_row][sc->rl_twist_col]);
+ if (++sc->rl_twist_col == 4) {
+ if (sc->rl_twist_row == 3)
+ sc->rl_twister = RECHK_LONG;
+ else
+ sc->rl_twister = DONE;
+ }
+ break;
+ case RECHK_LONG:
+ /*
+ * For long cables, we have to double check to make sure we
+ * don't mistune.
+ */
+ linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS;
+ if (linktest == RL_CSCFG_ROW3)
+ sc->rl_twister = DONE;
+ else {
+ CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_RETUNE);
+ sc->rl_twister = RETUNE;
+ }
+ break;
+ case RETUNE:
+ /* Retune for a shorter cable (try column 2) */
+ CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST);
+ CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF);
+ CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF);
+ CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET);
+ sc->rl_twist_row--;
+ sc->rl_twist_col = 0;
+ sc->rl_twister = SET_PARAM;
+ break;
+
+ case DONE:
+ break;
+ }
+
+}
+
+static void
rl_tick(void *xsc)
{
struct rl_softc *sc = xsc;
struct mii_data *mii;
+ int ticks;
RL_LOCK_ASSERT(sc);
mii = device_get_softc(sc->rl_miibus);
mii_tick(mii);
+ if (sc->rl_twister != DONE)
+ rl_twister_update(sc);
+ if (sc->rl_twister != DONE)
+ ticks = hz / 10;
+ else
+ ticks = hz;
rl_watchdog(sc);
- callout_reset(&sc->rl_stat_callout, hz, rl_tick, sc);
+ callout_reset(&sc->rl_stat_callout, ticks, rl_tick, sc);
}
#ifdef DEVICE_POLLING
@@ -1490,6 +1592,13 @@
rl_stop(sc);
/*
+ * Reset twister register tuning state. The twister registers
+ * and their tuning are undocumented, but are necessary to cope
+ * with bad links. rl_twister = DONE here will disable this entirely.
+ */
+ sc->rl_twister = CHK_LINK;
+
+ /*
* Init our MAC address. Even though the chipset
* documentation doesn't mention it, we need to enter "Config
* register write enable" mode to modify the ID registers.
diff -ur src/sys/pci/if_rlreg.h newcard/src/sys/pci/if_rlreg.h
--- src/sys/pci/if_rlreg.h 2008-08-23 22:21:15.000000000 -0600
+++ newcard/src/sys/pci/if_rlreg.h 2008-08-23 22:26:09.000000000 -0600
@@ -309,6 +309,27 @@
#define RL_CMD_RESET 0x0010
/*
+ * Twister register values. These are completely undocumented and derived
+ * from public sources.
+ */
+#define RL_CSCFG_LINK_OK 0x0400
+#define RL_CSCFG_CHANGE 0x0800
+#define RL_CSCFG_STATUS 0xf000
+#define RL_CSCFG_ROW3 0x7000
+#define RL_CSCFG_ROW2 0x3000
+#define RL_CSCFG_ROW1 0x1000
+#define RL_CSCFG_LINK_DOWN_OFF_CMD 0x03c0
+#define RL_CSCFG_LINK_DOWN_CMD 0xf3c0
+
+#define RL_NWAYTST_RESET 0
+#define RL_NWAYTST_CBL_TEST 0x20
+
+#define RL_PARA78 0x78
+#define RL_PARA78_DEF 0x78fa8388
+#define RL_PARA7C 0x7C
+#define RL_PARA7C_DEF 0xcb38de43
+#define RL_PARA7C_RETUNE 0xfb38de03
+/*
* EEPROM control register
*/
#define RL_EE_DATAOUT 0x01 /* Data out */
@@ -801,6 +822,8 @@
bus_addr_t rl_tx_list_addr;
};
+enum rl_twist { DONE, CHK_LINK, FIND_ROW, SET_PARAM, RECHK_LONG, RETUNE };
+
struct rl_softc {
struct ifnet *rl_ifp; /* interface info */
bus_space_handle_t rl_bhandle; /* bus space handle */
@@ -830,6 +853,9 @@
uint32_t rl_rxlenmask;
int rl_testmode;
int rl_if_flags;
+ enum rl_twist rl_twister;
+ int rl_twist_row;
+ int rl_twist_col;
int suspended; /* 0 = normal 1 = suspended */
#ifdef DEVICE_POLLING
int rxcycles;
@@ -850,6 +876,8 @@
#define RL_FLAG_LINK 0x8000
};
+
+
#define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx)
#define RL_UNLOCK(_sc) mtx_unlock(&(_sc)->rl_mtx)
#define RL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rl_mtx, MA_OWNED)
More information about the freebsd-net
mailing list