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
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)
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to