Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/ic - add DM9000 internal PHY manipulating routines.



details:   https://anonhg.NetBSD.org/src/rev/a111e0a148aa
branches:  trunk
changeset: 773117:a111e0a148aa
user:      nisimura <nisimura%NetBSD.org@localhost>
date:      Sat Jan 28 08:29:55 2012 +0000

description:
- add DM9000 internal PHY manipulating routines.
- now capable of auto-negotiation, use this as default media selection.
- add multicast filter support.
- code cleanup.

Code update from Paul Fleischer.

diffstat:

 sys/dev/ic/dm9000.c    |  736 ++++++++++++++++++++++++++++++++++--------------
 sys/dev/ic/dm9000reg.h |    6 +-
 sys/dev/ic/dm9000var.h |   21 +-
 3 files changed, 537 insertions(+), 226 deletions(-)

diffs (truncated from 1025 to 300 lines):

diff -r 5629c4b4bf16 -r a111e0a148aa sys/dev/ic/dm9000.c
--- a/sys/dev/ic/dm9000.c       Sat Jan 28 07:19:17 2012 +0000
+++ b/sys/dev/ic/dm9000.c       Sat Jan 28 08:29:55 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dm9000.c,v 1.3 2011/06/30 20:09:39 wiz Exp $   */
+/*     $NetBSD: dm9000.c,v 1.4 2012/01/28 08:29:55 nisimura Exp $      */
 
 /*
  * Copyright (c) 2009 Paul Fleischer
@@ -89,6 +89,7 @@
 #include <sys/cdefs.h>
 
 #include <sys/param.h>
+#include <sys/kernel.h>
 #include <sys/systm.h>
 #include <sys/mbuf.h>
 #include <sys/syslog.h>
@@ -117,7 +118,7 @@
 
 #if 1
 #undef DM9000_DEBUG
-#undef  DM9000_TX_DEBUG
+#undef DM9000_TX_DEBUG
 #undef DM9000_TX_DATA_DEBUG
 #undef DM9000_RX_DEBUG
 #undef  DM9000_RX_DATA_DEBUG
@@ -159,9 +160,13 @@
 #define TX_DATA_DPRINTF(s) do {} while (/*CONSTCOND*/0)
 #endif
 
-
+/*** Internal PHY functions ***/
 uint16_t dme_phy_read(struct dme_softc *sc, int reg);
-void dme_phy_write(struct dme_softc *sc, int reg, uint16_t value);
+void   dme_phy_write(struct dme_softc *sc, int reg, uint16_t value);
+void   dme_phy_init(struct dme_softc *sc);
+void   dme_phy_reset(struct dme_softc *sc);
+void   dme_phy_update_media(struct dme_softc *sc);
+void   dme_phy_check_link(void *arg);
 
 /*** Methods registered in struct ifnet ***/
 void   dme_start_output(struct ifnet *ifp);
@@ -186,6 +191,17 @@
 /* Software Initialize/Reset of the DM9000 */
 void    dme_reset(struct dme_softc *sc);
 
+/* Configure multicast filter */
+void   dme_set_addr_filter(struct dme_softc *sc);
+
+/* Set media */
+int    dme_set_media(struct dme_softc *sc, int media);
+
+/* Read/write packet data from/to DM9000 IC in various transfer sizes */
+int    dme_pkt_read_2(struct dme_softc *sc, struct ifnet *ifp, struct mbuf **outBuf);
+int    dme_pkt_write_2(struct dme_softc *sc, struct mbuf *bufChain);
+/* TODO: Implement 8 and 32 bit read/write functions */
+
 uint16_t
 dme_phy_read(struct dme_softc *sc, int reg)
 {
@@ -199,9 +215,6 @@
        /* Wait until access to PHY has completed */
        while (dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE);
 
-       /* XXX: The delay is probably not necessary as we just busy-waited */
-       delay(200);
-
        /* Reset ERPRR-bit */
        dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY);
 
@@ -228,19 +241,172 @@
        /* Wait until access to PHY has completed */
        while(dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE);
 
-
-       /* XXX: The delay is probably not necessary as we just busy-waited */
-       delay(200);
-
        /* Reset ERPRR-bit */
        dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY);
 }
 
-int
-dme_attach(struct dme_softc *sc, uint8_t *enaddr)
+void
+dme_phy_init(struct dme_softc *sc)
+{
+       u_int ifm_media = sc->sc_media.ifm_media;
+       uint32_t bmcr, anar;
+
+       bmcr = dme_phy_read(sc, DM9000_PHY_BMCR);
+       anar = dme_phy_read(sc, DM9000_PHY_ANAR);
+
+       anar = anar & ~DM9000_PHY_ANAR_10_HDX
+               & ~DM9000_PHY_ANAR_10_FDX
+               & ~DM9000_PHY_ANAR_TX_HDX
+               & ~DM9000_PHY_ANAR_TX_FDX;
+
+       switch (IFM_SUBTYPE(ifm_media)) {
+       case IFM_AUTO:
+               bmcr |= DM9000_PHY_BMCR_AUTO_NEG_EN;
+               anar |= DM9000_PHY_ANAR_10_HDX |
+                       DM9000_PHY_ANAR_10_FDX |
+                       DM9000_PHY_ANAR_TX_HDX |
+                       DM9000_PHY_ANAR_TX_FDX;
+               break;
+       case IFM_10_T:
+               //bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN;
+               bmcr &= ~DM9000_PHY_BMCR_SPEED_SELECT;
+               if (ifm_media & IFM_FDX)
+                       anar |= DM9000_PHY_ANAR_10_FDX;
+               else
+                       anar |= DM9000_PHY_ANAR_10_HDX;
+               break;
+       case IFM_100_TX:
+               //bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN;
+               bmcr |= DM9000_PHY_BMCR_SPEED_SELECT;
+               if (ifm_media & IFM_FDX)
+                       anar |= DM9000_PHY_ANAR_TX_FDX;
+               else
+                       anar |= DM9000_PHY_ANAR_TX_HDX;
+
+               break;
+       }
+
+       if(ifm_media & IFM_FDX) {
+               bmcr |= DM9000_PHY_BMCR_DUPLEX_MODE;
+       } else {
+               bmcr &= ~DM9000_PHY_BMCR_DUPLEX_MODE;
+       }
+
+       dme_phy_write(sc, DM9000_PHY_BMCR, bmcr);
+       dme_phy_write(sc, DM9000_PHY_ANAR, anar);
+}
+
+void
+dme_phy_reset(struct dme_softc *sc)
+{
+       uint32_t reg;
+
+       /* PHY Reset */
+       dme_phy_write(sc, DM9000_PHY_BMCR, DM9000_PHY_BMCR_RESET);
+
+       reg = dme_read(sc, DM9000_GPCR);
+       dme_write(sc, DM9000_GPCR, reg & ~DM9000_GPCR_GPIO0_OUT);
+       reg = dme_read(sc, DM9000_GPR);
+       dme_write(sc, DM9000_GPR, reg | DM9000_GPR_PHY_PWROFF);
+
+       dme_phy_init(sc);
+
+       reg = dme_read(sc, DM9000_GPR);
+       dme_write(sc, DM9000_GPR, reg & ~DM9000_GPR_PHY_PWROFF);
+       reg = dme_read(sc, DM9000_GPCR);
+       dme_write(sc, DM9000_GPCR, reg | DM9000_GPCR_GPIO0_OUT);
+
+       dme_phy_update_media(sc);
+}
+
+void
+dme_phy_update_media(struct dme_softc *sc)
 {
-       struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-       uint8_t b[2];
+       u_int ifm_media = sc->sc_media.ifm_media;
+       uint32_t reg;
+
+       if (IFM_SUBTYPE(ifm_media) == IFM_AUTO) {
+               /* If auto-negotiation is used, ensures that it is completed
+                before trying to extract any media information. */
+               reg = dme_phy_read(sc, DM9000_PHY_BMSR);
+               if ((reg & DM9000_PHY_BMSR_AUTO_NEG_AB) == 0) {
+                       /* Auto-negotation not possible, therefore there is no
+                          reason to try obtain any media information. */
+                       return;
+               }
+
+               /* Then loop until the negotiation is completed. */
+               while ((reg & DM9000_PHY_BMSR_AUTO_NEG_COM) == 0) {
+                       /* TODO: Bail out after a finite number of attempts
+                        in case something goes wrong. */
+                       preempt();
+                       reg = dme_phy_read(sc, DM9000_PHY_BMSR);
+               }
+       }
+
+
+       sc->sc_media_active = IFM_ETHER;
+       reg = dme_phy_read(sc, DM9000_PHY_BMCR);
+
+       if (reg & DM9000_PHY_BMCR_SPEED_SELECT) {
+               sc->sc_media_active |= IFM_100_TX;
+       } else {
+               sc->sc_media_active |= IFM_10_T;
+       }
+
+       if (reg & DM9000_PHY_BMCR_DUPLEX_MODE) {
+               sc->sc_media_active |= IFM_FDX;
+       }
+}
+
+void
+dme_phy_check_link(void *arg)
+{
+       struct dme_softc *sc = arg;
+       uint32_t reg;
+       int s;
+
+       s = splnet();
+
+       reg = dme_read(sc, DM9000_NSR) & DM9000_NSR_LINKST;
+
+       if( reg )
+               reg = IFM_ETHER | IFM_AVALID | IFM_ACTIVE;
+       else {
+               reg = IFM_ETHER | IFM_AVALID;
+               sc->sc_media_active = IFM_NONE;
+       }
+
+       if ( (sc->sc_media_status != reg) && (reg & IFM_ACTIVE)) {
+               dme_phy_reset(sc);
+       }
+
+       sc->sc_media_status = reg;
+
+       callout_schedule(&sc->sc_link_callout, mstohz(2000));
+       splx(s);
+}
+
+int
+dme_set_media(struct dme_softc *sc, int media)
+{
+       int s;
+
+       s = splnet();
+       sc->sc_media.ifm_media = media;
+       dme_phy_reset(sc);
+
+       splx(s);
+
+       return 0;
+}
+
+int
+dme_attach(struct dme_softc *sc, const uint8_t *enaddr)
+{
+       struct ifnet    *ifp = &sc->sc_ethercom.ec_if;
+       uint8_t         b[2];
+       uint16_t        io_mode;
 
        dme_read_c(sc, DM9000_VID0, b, 2);
 #if BYTE_ORDER == BIG_ENDIAN
@@ -260,16 +426,6 @@
                    sc->sc_product_id);
        }
 
-#if 0
-       {
-               /* Force 10Mbps to test dme_phy_write */
-               uint16_t bmcr;
-               bmcr = dme_phy_read(sc, DM9000_PHY_BMCR);
-               bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN;
-               bmcr &= ~DM9000_PHY_BMCR_SPEED_SELECT; /* select 100Mbps */
-               dme_phy_write(sc, DM9000_PHY_BMCR, bmcr);
-       }
-#endif
        /* Initialize ifnet structure. */
        strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
        ifp->if_softc = sc;
@@ -278,17 +434,28 @@
        ifp->if_ioctl = dme_ioctl;
        ifp->if_stop = dme_stop;
        ifp->if_watchdog = NULL;        /* no watchdog at this stage */
-       ifp->if_flags = IFF_SIMPLEX | IFF_NOTRAILERS |
-               IFF_BROADCAST; /* No multicast support for now */
+       ifp->if_flags = IFF_SIMPLEX | IFF_NOTRAILERS | IFF_BROADCAST |
+                       IFF_MULTICAST;
        IFQ_SET_READY(&ifp->if_snd);
 
        /* Initialize ifmedia structures. */
        ifmedia_init(&sc->sc_media, 0, dme_mediachange, dme_mediastatus);
-       ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T|IFM_100_TX, 0, NULL);
-       ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_10_T|IFM_100_TX);
+       ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_AUTO, 0, NULL);
+       ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
+       ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T, 0, NULL);
+       ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
+       ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_100_TX, 0, NULL);
+
+       ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
 
        if (enaddr != NULL)
                memcpy(sc->sc_enaddr, enaddr, sizeof(sc->sc_enaddr));
+       /* TODO: Support an EEPROM attached to the DM9000 chip */
+
+       callout_init(&sc->sc_link_callout, 0);
+       callout_setfunc(&sc->sc_link_callout, dme_phy_check_link, sc);
+
+       sc->sc_media_status = 0;
 
        /* Configure DM9000 with the MAC address */



Home | Main Index | Thread Index | Old Index