Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci o Add support for accessing the PHY through MDIO...



details:   https://anonhg.NetBSD.org/src/rev/a2ba116797fe
branches:  trunk
changeset: 555891:a2ba116797fe
user:      cube <cube%NetBSD.org@localhost>
date:      Wed Dec 03 21:58:49 2003 +0000

description:
o Add support for accessing the PHY through MDIO for recent SiS chips
o Add support for the recent SiS96x chipsets that have a new revision.
  That includes a new bit of code to access the EEPROM, since it is
  shared with the ieee1394 controller on those chipsets.

Mostly taken from FreeBSD (rev. 1.62 and 1.64 of sys/pci/if_sip.c).  I
tried to make the code look less ugly, but couldn't invent documentation.

Fix PR #23481.  Thanks to Stephane ENGEL <sengel AT melshake DOT com> for
the report and the cheerful testing.

diffstat:

 sys/dev/pci/if_sip.c    |  260 +++++++++++++++++++++++++++++++++++++++++++----
 sys/dev/pci/if_sipreg.h |   18 ++-
 2 files changed, 252 insertions(+), 26 deletions(-)

diffs (truncated from 390 to 300 lines):

diff -r 30016a9857af -r a2ba116797fe sys/dev/pci/if_sip.c
--- a/sys/dev/pci/if_sip.c      Wed Dec 03 20:24:51 2003 +0000
+++ b/sys/dev/pci/if_sip.c      Wed Dec 03 21:58:49 2003 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_sip.c,v 1.83 2003/10/29 03:31:22 mycroft Exp $      */
+/*     $NetBSD: if_sip.c,v 1.84 2003/12/03 21:58:49 cube Exp $ */
 
 /*-
  * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_sip.c,v 1.83 2003/10/29 03:31:22 mycroft Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_sip.c,v 1.84 2003/12/03 21:58:49 cube Exp $");
 
 #include "bpfilter.h"
 #include "rnd.h"
@@ -424,6 +424,7 @@
 void   SIP_DECL(dp83820_read_macaddr)(struct sip_softc *,
            const struct pci_attach_args *, u_int8_t *);
 #else
+static void    SIP_DECL(sis900_eeprom_delay)(struct sip_softc *sc);
 void   SIP_DECL(sis900_read_macaddr)(struct sip_softc *,
            const struct pci_attach_args *, u_int8_t *);
 void   SIP_DECL(dp83815_read_macaddr)(struct sip_softc *,
@@ -439,6 +440,8 @@
 void   SIP_DECL(dp83820_mii_writereg)(struct device *, int, int, int);
 void   SIP_DECL(dp83820_mii_statchg)(struct device *);
 #else
+static void    SIP_DECL(sis900_mii_sync)(struct sip_softc *);
+static void    SIP_DECL(sis900_mii_send)(struct sip_softc *, u_int32_t, int);
 int    SIP_DECL(sis900_mii_readreg)(struct device *, int, int);
 void   SIP_DECL(sis900_mii_writereg)(struct device *, int, int, int);
 void   SIP_DECL(sis900_mii_statchg)(struct device *);
@@ -2678,6 +2681,7 @@
                crc = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN);
 
                if (SIP_SIS900_REV(sc, SIS_REV_635) ||
+                   SIP_SIS900_REV(sc, SIS_REV_960) ||
                    SIP_SIS900_REV(sc, SIS_REV_900B)) {
                        /* Just want the 8 most significant bits. */
                        crc >>= 24;
@@ -2727,6 +2731,7 @@
                FILTER_EMIT(RFCR_RFADDR_MC6, mchash[6]);
                FILTER_EMIT(RFCR_RFADDR_MC7, mchash[7]);
                if (SIP_SIS900_REV(sc, SIS_REV_635) ||
+                   SIP_SIS900_REV(sc, SIS_REV_960) ||
                    SIP_SIS900_REV(sc, SIS_REV_900B)) {
                        FILTER_EMIT(RFCR_RFADDR_MC8, mchash[8]);
                        FILTER_EMIT(RFCR_RFADDR_MC9, mchash[9]);
@@ -3053,6 +3058,56 @@
        bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_EROMAR, val);
 }
 #else /* ! DP83820 */
+
+/* SiS MII functions */
+
+#define        SIS_SET_EROMAR(x,y)     bus_space_write_4(x->sc_st, x->sc_sh, SIP_EROMAR,       \
+                                   bus_space_read_4(x->sc_st, x->sc_sh, SIP_EROMAR) | (y))
+
+#define        SIS_CLR_EROMAR(x,y)     bus_space_write_4(x->sc_st, x->sc_sh, SIP_EROMAR,       \
+                                   bus_space_read_4(x->sc_st, x->sc_sh, SIP_EROMAR) & ~(y))
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void
+SIP_DECL(sis900_mii_sync)(struct sip_softc *sc)
+{
+       register int i;
+
+       SIS_SET_EROMAR(sc, EROMAR_MDDIR | EROMAR_MDIO);
+
+       for (i = 0; i < 32; i++) {
+               SIS_SET_EROMAR(sc, EROMAR_MDC);
+               DELAY(1);
+               SIS_CLR_EROMAR(sc, EROMAR_MDC);
+               DELAY(1);
+       }
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void
+SIP_DECL(sis900_mii_send)(struct sip_softc *sc, u_int32_t bits, int cnt)
+{
+       int i;
+
+       SIS_CLR_EROMAR(sc, EROMAR_MDC);
+
+       /* Send first cnt bits of 'bits' */
+       for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
+               if (bits & i)
+                       SIS_SET_EROMAR(sc, EROMAR_MDIO);
+               else
+                       SIS_CLR_EROMAR(sc, EROMAR_MDIO);
+               DELAY(1);
+               SIS_CLR_EROMAR(sc, EROMAR_MDC);
+               DELAY(1);
+               SIS_SET_EROMAR(sc, EROMAR_MDC);
+       }
+}
+
 /*
  * sip_sis900_mii_readreg:     [mii interface function]
  *
@@ -3062,23 +3117,90 @@
 SIP_DECL(sis900_mii_readreg)(struct device *self, int phy, int reg)
 {
        struct sip_softc *sc = (struct sip_softc *) self;
-       u_int32_t enphy;
+       u_int32_t ack, val = 0;
+       int s, i;
 
        /*
         * The SiS 900 has only an internal PHY on the MII.  Only allow
         * MII address 0.
         */
-       if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
-           sc->sc_rev < SIS_REV_635 && phy != 0)
-               return (0);
-
-       bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
-           (phy << ENPHY_PHYADDR_SHIFT) | (reg << ENPHY_REGADDR_SHIFT) |
-           ENPHY_RWCMD | ENPHY_ACCESS);
-       do {
-               enphy = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
-       } while (enphy & ENPHY_ACCESS);
-       return ((enphy & ENPHY_PHYDATA) >> ENPHY_DATA_SHIFT);
+       if (sc->sc_model->sip_product != PCI_PRODUCT_SIS_900 ||
+           sc->sc_rev < SIS_REV_635) {
+               if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 && phy != 0)
+                       return (0);
+
+               bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
+                   (phy << ENPHY_PHYADDR_SHIFT) | (reg << ENPHY_REGADDR_SHIFT) |
+                   ENPHY_RWCMD | ENPHY_ACCESS);
+               do {
+                       val = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
+               } while (val & ENPHY_ACCESS);
+               return ((val & ENPHY_PHYDATA) >> ENPHY_DATA_SHIFT);
+       }
+
+       s = splnet();
+
+       /* Use mdio access from FreeBSD (apparently inspired by Linux) */
+       SIS_SET_EROMAR(sc, EROMAR_MDDIR);
+
+       SIP_DECL(sis900_mii_sync)(sc);
+
+       /*
+        * Send command/address info.
+        */
+       SIP_DECL(sis900_mii_send)(sc, SIS_MII_STARTDELIM, 2);
+       SIP_DECL(sis900_mii_send)(sc, SIS_MII_READOP, 2);
+       SIP_DECL(sis900_mii_send)(sc, phy, 5);
+       SIP_DECL(sis900_mii_send)(sc, reg, 5);
+ 
+       /* Idle bit */
+       SIS_CLR_EROMAR(sc, EROMAR_MDC | EROMAR_MDIO);
+       DELAY(1);
+       SIS_SET_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+ 
+       /* Turn off xmit. */
+       SIS_CLR_EROMAR(sc, EROMAR_MDDIR);
+ 
+       /* Check for ack */
+       SIS_CLR_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+
+       ack = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_EROMAR) & EROMAR_MDIO;
+
+       SIS_SET_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+ 
+       /*
+        * Now try reading data bits. If the ack failed, we still
+        * need to clock through 16 cycles to keep the PHY(s) in sync.
+        */
+       if (ack)
+               for (i = 0; i < 16; i++) {
+                       SIS_CLR_EROMAR(sc, EROMAR_MDC);
+                       DELAY(1);
+                       SIS_SET_EROMAR(sc, EROMAR_MDC);
+                       DELAY(1);
+               }
+       else
+               for (i = 0x8000; i; i >>= 1) {
+                       SIS_CLR_EROMAR(sc, EROMAR_MDC);
+                       DELAY(1);
+                       if (bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_EROMAR) & EROMAR_MDIO)
+                               val |= i;
+                       DELAY(1);
+                       SIS_SET_EROMAR(sc, EROMAR_MDC);
+                       DELAY(1);
+               }
+
+       SIS_CLR_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+       SIS_SET_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+
+       splx(s);
+
+       return(val);
 }
 
 /*
@@ -3091,21 +3213,54 @@
 {
        struct sip_softc *sc = (struct sip_softc *) self;
        u_int32_t enphy;
+       int s;
 
        /*
         * The SiS 900 has only an internal PHY on the MII.  Only allow
         * MII address 0.
         */
-       if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 &&
-           sc->sc_rev < SIS_REV_635 && phy != 0)
+       if (sc->sc_model->sip_product != PCI_PRODUCT_SIS_900 ||
+           sc->sc_rev < SIS_REV_635) {
+               if (sc->sc_model->sip_product == PCI_PRODUCT_SIS_900 && phy != 0)
+                       return;
+
+               bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
+                   (val << ENPHY_DATA_SHIFT) | (phy << ENPHY_PHYADDR_SHIFT) |
+                   (reg << ENPHY_REGADDR_SHIFT) | ENPHY_ACCESS);
+               do {
+                       enphy = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
+               } while (enphy & ENPHY_ACCESS);
                return;
-
-       bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_ENPHY,
-           (val << ENPHY_DATA_SHIFT) | (phy << ENPHY_PHYADDR_SHIFT) |
-           (reg << ENPHY_REGADDR_SHIFT) | ENPHY_ACCESS);
-       do {
-               enphy = bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_ENPHY);
-       } while (enphy & ENPHY_ACCESS);
+       }
+
+       s = splnet();
+
+       /*
+        * Turn on data output.
+        */
+       SIS_SET_EROMAR(sc, EROMAR_MDDIR);
+ 
+       SIP_DECL(sis900_mii_sync)(sc);
+ 
+       SIP_DECL(sis900_mii_send)(sc, SIS_MII_STARTDELIM, 2);
+       SIP_DECL(sis900_mii_send)(sc, SIS_MII_WRITEOP, 2);
+       SIP_DECL(sis900_mii_send)(sc, phy, 5);
+       SIP_DECL(sis900_mii_send)(sc, reg, 5);
+       SIP_DECL(sis900_mii_send)(sc, SIS_MII_TURNAROUND, 2);
+       SIP_DECL(sis900_mii_send)(sc, val, 16);
+ 
+       /* Idle bit. */
+       SIS_SET_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+       SIS_CLR_EROMAR(sc, EROMAR_MDC);
+       DELAY(1);
+ 
+       /*
+        * Turn off xmit.
+        */
+       SIS_CLR_EROMAR(sc, EROMAR_MDDIR);
+ 
+       splx(s);
 }
 
 /*
@@ -3309,6 +3464,19 @@
        enaddr[5] = eeprom_data[SIP_DP83820_EEPROM_PMATCH0 / 2] >> 8;
 }
 #else /* ! DP83820 */
+static void
+SIP_DECL(sis900_eeprom_delay)(struct sip_softc *sc)
+{
+       int i;
+
+       /*
+        * FreeBSD goes from (300/33)+1 [10] to 0.  There must be
+        * a reason, but I don't know it.
+        */
+       for (i = 0; i < 10; i++)
+               bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_CR);
+}
+
 void
 SIP_DECL(sis900_read_macaddr)(struct sip_softc *sc,
     const struct pci_attach_args *pa, u_int8_t *enaddr)
@@ -3345,6 +3513,52 @@
                    0xffff;
                break;
 
+       case SIS_REV_960:



Home | Main Index | Thread Index | Old Index