Author: imp
Date: Fri Oct 31 23:24:13 2008
New Revision: 184515
URL: http://svn.freebsd.org/changeset/base/184515

Log:
  Add RL_TWISTER_ENABLE option.  This enables the magic bits to do long
  cable tuning.  This has helped in some installations for hardware
  deployed by a former employer.  Made optional because the lists aren't
  full of complaints about these cards... even when they were wildly
  popular.
  
  Reviewed by:  attilio@, jhb@, trhodes@ (all an older version of the patch)

Modified:
  head/sys/conf/options
  head/sys/modules/rl/Makefile
  head/sys/pci/if_rl.c
  head/sys/pci/if_rlreg.h

Modified: head/sys/conf/options
==============================================================================
--- head/sys/conf/options       Fri Oct 31 18:40:35 2008        (r184514)
+++ head/sys/conf/options       Fri Oct 31 23:24:13 2008        (r184515)
@@ -672,6 +672,9 @@ ED_SIC                      opt_ed.h
 # bce driver
 BCE_DEBUG              opt_bce.h
 
+# rl driver
+RL_TWISTER_ENABLE      opt_rl.h
+
 SOCKBUF_DEBUG          opt_global.h
 
 # options for ubsec driver

Modified: head/sys/modules/rl/Makefile
==============================================================================
--- head/sys/modules/rl/Makefile        Fri Oct 31 18:40:35 2008        
(r184514)
+++ head/sys/modules/rl/Makefile        Fri Oct 31 23:24:13 2008        
(r184515)
@@ -3,7 +3,7 @@
 .PATH: ${.CURDIR}/../../pci
 
 KMOD=  if_rl
-SRCS=  if_rl.c device_if.h bus_if.h pci_if.h
+SRCS=  if_rl.c device_if.h bus_if.h pci_if.h opt_rl.h
 SRCS+= miibus_if.h
 
 .include <bsd.kmod.mk>

Modified: head/sys/pci/if_rl.c
==============================================================================
--- head/sys/pci/if_rl.c        Fri Oct 31 18:40:35 2008        (r184514)
+++ head/sys/pci/if_rl.c        Fri Oct 31 23:24:13 2008        (r184515)
@@ -85,6 +85,7 @@ __FBSDID("$FreeBSD$");
 
 #ifdef HAVE_KERNEL_OPTION_HEADERS
 #include "opt_device_polling.h"
+#include "opt_rl.h"
 #endif
 
 #include <sys/param.h>
@@ -1383,19 +1384,143 @@ rl_txeof(struct rl_softc *sc)
                sc->rl_watchdog_timer = 0;
 }
 
+#ifdef RL_TWISTER_ENABLE
+static void
+rl_twister_update(struct rl_softc *sc)
+{
+       uint16_t linktest;
+       /*
+        * Table provided by RealTek (Kinston <[EMAIL PROTECTED]>) for
+        * Linux driver.  Values undocumented otherwise.
+        */
+       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;
+       }
+       
+}
+#endif
+
 static void
 rl_tick(void *xsc)
 {
        struct rl_softc         *sc = xsc;
        struct mii_data         *mii;
+       int ticks;
 
        RL_LOCK_ASSERT(sc);
+       /*
+        * If we're doing the twister cable calibration, then we need to defer
+        * watchdog timeouts.  This is a no-op in normal operations, but
+        * can falsely trigger when the cable calibration takes a while and
+        * there was traffic ready to go when rl was started.
+        *
+        * We don't defer mii_tick since that updates the mii status, which
+        * helps the twister process, at least according to similar patches
+        * for the Linux driver I found online while doing the fixes.  Worst
+        * case is a few extra mii reads during calibration.
+        */
        mii = device_get_softc(sc->rl_miibus);
        mii_tick(mii);
-
+#ifdef RL_TWISTER_ENABLE
+       if (sc->rl_twister == DONE)
+               rl_watchdog(sc);
+       else
+               rl_twister_update(sc);
+       if (sc->rl_twister == DONE)
+               ticks = hz;
+       else
+               ticks = hz / 10;
+#else
        rl_watchdog(sc);
+       ticks = hz;
+#endif
 
-       callout_reset(&sc->rl_stat_callout, hz, rl_tick, sc);
+       callout_reset(&sc->rl_stat_callout, ticks, rl_tick, sc);
 }
 
 #ifdef DEVICE_POLLING
@@ -1643,6 +1768,14 @@ rl_init_locked(struct rl_softc *sc)
        rl_stop(sc);
 
        rl_reset(sc);
+#ifdef RL_TWISTER_ENABLE
+       /*
+        * 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;
+#endif
 
        /*
         * Init our MAC address.  Even though the chipset

Modified: head/sys/pci/if_rlreg.h
==============================================================================
--- head/sys/pci/if_rlreg.h     Fri Oct 31 18:40:35 2008        (r184514)
+++ head/sys/pci/if_rlreg.h     Fri Oct 31 23:24:13 2008        (r184515)
@@ -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 */
@@ -809,6 +830,10 @@ struct rl_list_data {
        bus_addr_t              rl_tx_list_addr;
 };
 
+#ifdef RL_TWISTER_ENABLE
+enum rl_twist { DONE, CHK_LINK, FIND_ROW, SET_PARAM, RECHK_LONG, RETUNE };
+#endif
+
 struct rl_softc {
        struct ifnet            *rl_ifp;        /* interface info */
        bus_space_handle_t      rl_bhandle;     /* bus space handle */
@@ -837,6 +862,11 @@ struct rl_softc {
        uint32_t                rl_rxlenmask;
        int                     rl_testmode;
        int                     rl_if_flags;
+#ifdef RL_TWISTER_ENABLE
+       enum rl_twist           rl_twister;
+       int                     rl_twist_row;
+       int                     rl_twist_col;
+#endif
        int                     suspended;      /* 0 = normal  1 = suspended */
 #ifdef DEVICE_POLLING
        int                     rxcycles;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to