Author: marius
Date: Thu Oct 14 22:01:40 2010
New Revision: 213878
URL: http://svn.freebsd.org/changeset/base/213878

Log:
  Add a NetBSD-compatible mii_attach(), which is intended to eventually
  replace mii_phy_probe() altogether. Compared to the latter the advantages
  of mii_attach() are:
  - intended to be called multiple times in order to attach PHYs in multiple
    passes (f.e. in order to only use sub-ranges of the 0 to MII_NPHY - 1
    range)
  - being able to pass along the capability mask from the NIC to the PHY
    drivers
  - being able to specify at which address (phyloc) to probe for a PHY
    (instead of always probing at all addresses from 0 to MII_NPHY - 1)
  - being able to specify which PHY instance (offloc) to attach
  - being able to pass along MIIF_* flags from the NIC to the PHY drivers
    (f.e. as required to indicated to the PHY drivers that flow control is
    supported by the NIC driver, which actually is the motivation for this
    change).
  
  While at it, I used the opportunity to get rid of some hacks in mii(4)
  like miibus_probe() generally doing work besides sheer probing and the
  "EVIL HACK" (which will vanish entirely along with mii_phy_probe()) by
  passing the struct ifnet pointer via an argument of mii_attach() as well
  as to fix some resource leaks in mii(4) in case something fails.
  Commits which will update the PHY drivers to honor the MII flags passed
  down from the NIC drivers and take advantage of mii_attach() to get rid
  of certain types of hacks in NIC and PHY drivers as well as a conversion
  of the remaining uses of mii_phy_probe() will follow shortly.
  
  Reviewed by:  jhb, yongari
  Obtained from:        NetBSD (partially)

Modified:
  head/sys/dev/mii/mii.c
  head/sys/dev/mii/mii.h
  head/sys/dev/mii/miivar.h
  head/sys/modules/mii/Makefile

Modified: head/sys/dev/mii/mii.c
==============================================================================
--- head/sys/dev/mii/mii.c      Thu Oct 14 21:58:51 2010        (r213877)
+++ head/sys/dev/mii/mii.c      Thu Oct 14 22:01:40 2010        (r213878)
@@ -58,6 +58,8 @@ MODULE_VERSION(miibus, 1);
 #include "miibus_if.h"
 
 static int miibus_print_child(device_t dev, device_t child);
+static int miibus_read_ivar(device_t dev, device_t child, int which,
+    uintptr_t *result);
 static int miibus_child_location_str(device_t bus, device_t child, char *buf,
     size_t buflen);
 static int miibus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
@@ -77,6 +79,7 @@ static device_method_t miibus_methods[] 
 
        /* bus interface */
        DEVMETHOD(bus_print_child,      miibus_print_child),
+       DEVMETHOD(bus_read_ivar,        miibus_read_ivar),
        DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
        DEVMETHOD(bus_child_pnpinfo_str, miibus_child_pnpinfo_str),
        DEVMETHOD(bus_child_location_str, miibus_child_location_str),
@@ -100,87 +103,52 @@ driver_t miibus_driver = {
 };
 
 struct miibus_ivars {
+       struct ifnet    *ifp;
        ifm_change_cb_t ifmedia_upd;
        ifm_stat_cb_t   ifmedia_sts;
+       int             mii_flags;
 };
 
-/*
- * Helper function used by network interface drivers, attaches PHYs
- * to the network interface driver parent.
- */
 int
 miibus_probe(device_t dev)
 {
-       struct mii_attach_args  ma, *args;
-       struct mii_data         *mii;
-       device_t                child = NULL, parent;
-       int                     bmsr, capmask = 0xFFFFFFFF;
-
-       mii = device_get_softc(dev);
-       parent = device_get_parent(dev);
-       LIST_INIT(&mii->mii_phys);
-
-       for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) {
-               /*
-                * Check to see if there is a PHY at this address.  Note,
-                * many braindead PHYs report 0/0 in their ID registers,
-                * so we test for media in the BMSR.
-                */
-               bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR);
-               if (bmsr == 0 || bmsr == 0xffff ||
-                   (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
-                       /* Assume no PHY at this address. */
-                       continue;
-               }
-
-               /*
-                * Extract the IDs. Braindead PHYs will be handled by
-                * the `ukphy' driver, as we have no ID information to
-                * match on.
-                */
-               ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno,
-                   MII_PHYIDR1);
-               ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno,
-                   MII_PHYIDR2);
-
-               ma.mii_data = mii;
-               ma.mii_capmask = capmask;
-
-               args = malloc(sizeof(struct mii_attach_args),
-                   M_DEVBUF, M_NOWAIT);
-               bcopy((char *)&ma, (char *)args, sizeof(ma));
-               child = device_add_child(dev, NULL, -1);
-               device_set_ivars(child, args);
-       }
-
-       if (child == NULL)
-               return (ENXIO);
 
        device_set_desc(dev, "MII bus");
 
-       return (0);
+       return (BUS_PROBE_SPECIFIC);
 }
 
 int
 miibus_attach(device_t dev)
 {
        struct miibus_ivars     *ivars;
+       struct mii_attach_args  *ma;
        struct mii_data         *mii;
+       device_t                *children;
+       int                     i, nchildren;
 
        mii = device_get_softc(dev);
-       /*
-        * Note that each NIC's softc must start with an ifnet pointer.
-        * XXX: EVIL HACK!
-        */
-       mii->mii_ifp = *(struct 
ifnet**)device_get_softc(device_get_parent(dev));
-       mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE;
-       mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE;
+       nchildren = 0;
+       if (device_get_children(dev, &children, &nchildren) == 0) {
+               for (i = 0; i < nchildren; i++) {
+                       ma = device_get_ivars(children[i]);
+                       ma->mii_data = mii;
+               }
+               free(children, M_TEMP);
+       }
+       if (nchildren == 0) {
+               device_printf(dev, "cannot get children");
+               return (ENXIO);
+       }
        ivars = device_get_ivars(dev);
        ifmedia_init(&mii->mii_media, IFM_IMASK, ivars->ifmedia_upd,
            ivars->ifmedia_sts);
-       bus_generic_attach(dev);
+       mii->mii_ifp = ivars->ifp;
+       mii->mii_ifp->if_capabilities |= IFCAP_LINKSTATE;
+       mii->mii_ifp->if_capenable |= IFCAP_LINKSTATE;
+       LIST_INIT(&mii->mii_phys);
 
-       return (0);
+       return (bus_generic_attach(dev));
 }
 
 int
@@ -211,7 +179,28 @@ miibus_print_child(device_t dev, device_
 }
 
 static int
-miibus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
+miibus_read_ivar(device_t dev, device_t child __unused, int which,
+    uintptr_t *result)
+{
+       struct miibus_ivars *ivars;
+
+       /*
+        * NB: this uses the instance variables of the miibus rather than
+        * its PHY children.
+        */
+       ivars = device_get_ivars(dev);
+       switch (which) {
+       case MIIBUS_IVAR_FLAGS:
+               *result = ivars->mii_flags;
+               break;
+       default:
+               return (ENOENT);
+       }
+       return (0);
+}
+
+static int
+miibus_child_pnpinfo_str(device_t bus __unused, device_t child, char *buf,
     size_t buflen)
 {
        struct mii_attach_args *ma;
@@ -224,7 +213,7 @@ miibus_child_pnpinfo_str(device_t bus, d
 }
 
 static int
-miibus_child_location_str(device_t bus, device_t child, char *buf,
+miibus_child_location_str(device_t bus __unused, device_t child, char *buf,
     size_t buflen)
 {
        struct mii_attach_args *ma;
@@ -307,40 +296,177 @@ miibus_mediainit(device_t dev)
        ifmedia_set(&mii->mii_media, media);
 }
 
+/*
+ * Helper function used by network interface drivers, attaches the miibus and
+ * the PHYs to the network interface driver parent.
+ */
 int
-mii_phy_probe(device_t dev, device_t *child, ifm_change_cb_t ifmedia_upd,
-    ifm_stat_cb_t ifmedia_sts)
-{
-       struct miibus_ivars     *ivars;
-       int                     bmsr, i;
+mii_attach(device_t dev, device_t *miibus, struct ifnet *ifp,
+    ifm_change_cb_t ifmedia_upd, ifm_stat_cb_t ifmedia_sts, int capmask,
+    int phyloc, int offloc, int flags)
+{
+       struct miibus_ivars *ivars;
+       struct mii_attach_args ma, *args;
+       device_t *children, phy;
+       int bmsr, first, i, nchildren, offset, phymax, phymin, rv;
+
+       if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) {
+               printf("%s: phyloc and offloc specified", __func__);
+               return (EINVAL);
+       }
 
-       ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT);
-       if (ivars == NULL)
-               return (ENOMEM);
-       ivars->ifmedia_upd = ifmedia_upd;
-       ivars->ifmedia_sts = ifmedia_sts;
-       *child = device_add_child(dev, "miibus", -1);
-       device_set_ivars(*child, ivars);
-
-       for (i = 0; i < MII_NPHY; i++) {
-               bmsr = MIIBUS_READREG(dev, i, MII_BMSR);
-                if (bmsr == 0 || bmsr == 0xffff ||
-                    (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
-                        /* Assume no PHY at this address. */
-                        continue;
-                } else
-                       break;
+       if (offloc != MII_OFFSET_ANY && (offloc < 0 || offloc >= MII_NPHY)) {
+               printf("%s: ivalid offloc %d", __func__, offloc);
+               return (EINVAL);
        }
 
-       if (i == MII_NPHY) {
-               device_delete_child(dev, *child);
-               *child = NULL;
-               return (ENXIO);
+       if (phyloc == MII_PHY_ANY) {
+               phymin = 0;
+               phymax = MII_NPHY - 1;
+       } else {
+               if (phyloc < 0 || phyloc >= MII_NPHY) {
+                       printf("%s: ivalid phyloc %d", __func__, phyloc);
+                       return (EINVAL);
+               }
+               phymin = phymax = phyloc;
        }
 
-       bus_generic_attach(dev);
+       first = 0;
+       if (*miibus == NULL) {
+               first = 1;
+               ivars = malloc(sizeof(*ivars), M_DEVBUF, M_NOWAIT);
+               if (ivars == NULL)
+                       return (ENOMEM);
+               ivars->ifp = ifp;
+               ivars->ifmedia_upd = ifmedia_upd;
+               ivars->ifmedia_sts = ifmedia_sts;
+               ivars->mii_flags = flags;
+               *miibus = device_add_child(dev, "miibus", -1);
+               if (*miibus == NULL) {
+                       rv = ENXIO;
+                       goto fail;
+               }
+               device_set_ivars(*miibus, ivars);
+       } else {
+               ivars = device_get_ivars(*miibus);
+               if (ivars->ifp != ifp || ivars->ifmedia_upd != ifmedia_upd ||
+                   ivars->ifmedia_sts != ifmedia_sts ||
+                   ivars->mii_flags != flags) {
+                       printf("%s: non-matching invariant", __func__);
+                       return (EINVAL);
+               }
+               /*
+                * Assignment of the attach arguments mii_data for the first
+                * pass is done in miibus_attach(), i.e. once the miibus softc
+                * has been allocated.
+                */
+               ma.mii_data = device_get_softc(*miibus);
+       } 
+
+       ma.mii_capmask = capmask;
+
+       phy = NULL;
+       offset = 0;
+       for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
+               /*
+                * Make sure we haven't already configured a PHY at this
+                * address.  This allows mii_attach() to be called
+                * multiple times.
+                */
+               if (device_get_children(*miibus, &children, &nchildren) == 0) {
+                       for (i = 0; i < nchildren; i++) {
+                               args = device_get_ivars(children[i]);
+                               if (args->mii_phyno == ma.mii_phyno) {
+                                       /*
+                                        * Yes, there is already something
+                                        * configured at this address.
+                                        */
+                                       free(children, M_TEMP);
+                                       goto skip;
+                               }
+                       }
+                       free(children, M_TEMP);
+               }
+
+               /*
+                * Check to see if there is a PHY at this address.  Note,
+                * many braindead PHYs report 0/0 in their ID registers,
+                * so we test for media in the BMSR.
+                */
+               bmsr = MIIBUS_READREG(dev, ma.mii_phyno, MII_BMSR);
+               if (bmsr == 0 || bmsr == 0xffff ||
+                   (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) {
+                       /* Assume no PHY at this address. */
+                       continue;
+               }
+
+               /*
+                * There is a PHY at this address.  If we were given an
+                * `offset' locator, skip this PHY if it doesn't match.
+                */
+               if (offloc != MII_OFFSET_ANY && offloc != offset)
+                       goto skip;
+
+               /*
+                * Extract the IDs. Braindead PHYs will be handled by
+                * the `ukphy' driver, as we have no ID information to
+                * match on.
+                */
+               ma.mii_id1 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR1);
+               ma.mii_id2 = MIIBUS_READREG(dev, ma.mii_phyno, MII_PHYIDR2);
+
+               args = malloc(sizeof(struct mii_attach_args), M_DEVBUF,
+                   M_NOWAIT);
+               if (args == NULL)
+                       goto skip;
+               bcopy((char *)&ma, (char *)args, sizeof(ma));
+               phy = device_add_child(*miibus, NULL, -1);
+               if (phy == NULL) {
+                       free(args, M_DEVBUF);
+                       goto skip;
+               }
+               device_set_ivars(phy, args);
+ skip:
+               offset++;
+       }
+
+       if (first != 0) {
+               if (phy == NULL) {
+                       rv = ENXIO;
+                       goto fail;
+               }
+               rv = bus_generic_attach(dev);
+               if (rv != 0)
+                       goto fail;
+       }
+       rv = bus_generic_attach(*miibus);
+       if (rv != 0)
+               goto fail;
 
        return (0);
+
+ fail:
+       if (*miibus != NULL)
+               device_delete_child(dev, *miibus);
+       free(ivars, M_DEVBUF);
+       if (first != 0)
+               *miibus = NULL;
+       return (rv);
+}
+
+int
+mii_phy_probe(device_t dev, device_t *child, ifm_change_cb_t ifmedia_upd,
+    ifm_stat_cb_t ifmedia_sts)
+{
+       struct ifnet *ifp;
+
+       /*
+        * Note that each NIC's softc must start with an ifnet pointer.
+        * XXX: EVIL HACK!
+        */
+       ifp = *(struct ifnet **)device_get_softc(dev);
+       return (mii_attach(dev, child, ifp, ifmedia_upd, ifmedia_sts,
+           BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0));
 }
 
 /*

Modified: head/sys/dev/mii/mii.h
==============================================================================
--- head/sys/dev/mii/mii.h      Thu Oct 14 21:58:51 2010        (r213877)
+++ head/sys/dev/mii/mii.h      Thu Oct 14 22:01:40 2010        (r213878)
@@ -1,5 +1,5 @@
 /*     $NetBSD: mii.h,v 1.9 2001/05/31 03:07:14 thorpej Exp $  */
- 
+
 /*-
  * Copyright (c) 1997 Manuel Bouyer.  All rights reserved.
  *
@@ -47,7 +47,7 @@
 #define        MII_COMMAND_WRITE       0x01
 #define        MII_COMMAND_ACK         0x02
 
-#define        MII_BMCR        0x00    /* Basic mode control register (rw) */
+#define        MII_BMCR        0x00    /* Basic mode control register (rw) */
 #define        BMCR_RESET      0x8000  /* reset */
 #define        BMCR_LOOP       0x4000  /* loopback */
 #define        BMCR_SPEED0     0x2000  /* speed selection (LSB) */
@@ -82,6 +82,8 @@
 #define        BMSR_JABBER     0x0002  /* Jabber detected */
 #define        BMSR_EXTCAP     0x0001  /* Extended capability */
 
+#define        BMSR_DEFCAPMASK 0xffffffff
+
 /*
  * Note that the EXTSTAT bit indicates that there is extended status
  * info available in register 15, but 802.3 section 22.2.4.3 also

Modified: head/sys/dev/mii/miivar.h
==============================================================================
--- head/sys/dev/mii/miivar.h   Thu Oct 14 21:58:51 2010        (r213877)
+++ head/sys/dev/mii/miivar.h   Thu Oct 14 22:01:40 2010        (r213878)
@@ -102,7 +102,7 @@ typedef     int (*mii_downcall_t)(struct mii
  */
 struct mii_softc {
        device_t mii_dev;               /* generic device glue */
-       
+
        LIST_ENTRY(mii_softc) mii_list; /* entry on parent's PHY list */
 
        int mii_phy;                    /* our MII address */
@@ -122,16 +122,22 @@ struct mii_softc {
 typedef struct mii_softc mii_softc_t;
 
 /* mii_flags */
-#define        MIIF_INITDONE   0x0001          /* has been initialized 
(mii_data) */
-#define        MIIF_NOISOLATE  0x0002          /* do not isolate the PHY */
-#define        MIIF_NOLOOP     0x0004          /* no loopback capability */
-#define MIIF_AUTOTSLEEP        0x0010          /* use tsleep(), not callout() 
*/
-#define MIIF_HAVEFIBER 0x0020          /* from parent: has fiber interface */
-#define        MIIF_HAVE_GTCR  0x0040          /* has 100base-T2/1000base-T CR 
*/
-#define        MIIF_IS_1000X   0x0080          /* is a 1000BASE-X device */
-#define        MIIF_DOPAUSE    0x0100          /* advertise PAUSE capability */
-#define        MIIF_IS_HPNA    0x0200          /* is a HomePNA device */
-#define        MIIF_FORCEANEG  0x0400          /* force auto-negotiation */
+#define        MIIF_INITDONE   0x00000001      /* has been initialized 
(mii_data) */
+#define        MIIF_NOISOLATE  0x00000002      /* do not isolate the PHY */
+#define        MIIF_NOLOOP     0x00000004      /* no loopback capability */
+#define        MIIF_AUTOTSLEEP 0x00000010      /* use tsleep(), not callout() 
*/
+#define        MIIF_HAVEFIBER  0x00000020      /* from parent: has fiber 
interface */
+#define        MIIF_HAVE_GTCR  0x00000040      /* has 100base-T2/1000base-T CR 
*/
+#define        MIIF_IS_1000X   0x00000080      /* is a 1000BASE-X device */
+#define        MIIF_DOPAUSE    0x00000100      /* advertise PAUSE capability */
+#define        MIIF_IS_HPNA    0x00000200      /* is a HomePNA device */
+#define        MIIF_FORCEANEG  0x00000400      /* force auto-negotiation */
+#define        MIIF_MACPRIV0   0x01000000      /* private to the MAC driver */
+#define        MIIF_MACPRIV1   0x02000000      /* private to the MAC driver */
+#define        MIIF_MACPRIV2   0x04000000      /* private to the MAC driver */
+#define        MIIF_PHYPRIV0   0x10000000      /* private to the PHY driver */
+#define        MIIF_PHYPRIV1   0x20000000      /* private to the PHY driver */
+#define        MIIF_PHYPRIV2   0x40000000      /* private to the PHY driver */
 
 /* Default mii_anegticks values */
 #define        MII_ANEGTICKS           5
@@ -140,6 +146,14 @@ typedef struct mii_softc mii_softc_t;
 #define        MIIF_INHERIT_MASK       
(MIIF_NOISOLATE|MIIF_NOLOOP|MIIF_AUTOTSLEEP)
 
 /*
+ * Special `locators' passed to mii_attach().  If one of these is not
+ * an `any' value, we look for *that* PHY and configure it.  If both
+ * are not `any', that is an error, and mii_attach() will panic.
+ */
+#define        MII_OFFSET_ANY          -1
+#define        MII_PHY_ANY             -1
+
+/*
  * Used to attach a PHY to a parent.
  */
 struct mii_attach_args {
@@ -192,6 +206,18 @@ struct mii_media {
 #define PHY_WRITE(p, r, v) \
        MIIBUS_WRITEREG((p)->mii_dev, (p)->mii_phy, (r), (v))
 
+enum miibus_device_ivars {
+       MIIBUS_IVAR_FLAGS
+};
+
+/*
+ * Simplified accessors for miibus
+ */
+#define        MIIBUS_ACCESSOR(var, ivar, type)                                
\
+       __BUS_ACCESSOR(miibus, var, MIIBUS, ivar, type)
+
+MIIBUS_ACCESSOR(flags,         FLAGS,          int)
+
 extern devclass_t      miibus_devclass;
 extern driver_t                miibus_driver;
 
@@ -199,6 +225,8 @@ int miibus_probe(device_t);
 int    miibus_attach(device_t);
 int    miibus_detach(device_t);
 
+int    mii_attach(device_t, device_t *, struct ifnet *, ifm_change_cb_t,
+           ifm_stat_cb_t, int, int, int, int);
 int    mii_anar(int);
 void   mii_down(struct mii_data *);
 int    mii_mediachg(struct mii_data *);

Modified: head/sys/modules/mii/Makefile
==============================================================================
--- head/sys/modules/mii/Makefile       Thu Oct 14 21:58:51 2010        
(r213877)
+++ head/sys/modules/mii/Makefile       Thu Oct 14 22:01:40 2010        
(r213878)
@@ -12,7 +12,8 @@ SRCS+=        rgephy.c rlphy.c ruephy.c tdkphy.
 SRCS+= ukphy_subr.c
 SRCS+= xmphy.c
 
-EXPORT_SYMS=   mii_mediachg    \
+EXPORT_SYMS=   mii_attach      \
+               mii_mediachg    \
                mii_phy_probe   \
                mii_phy_reset   \
                mii_pollstat    \
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to