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