Author: gonzo
Date: Wed Aug 15 04:03:55 2012
New Revision: 239275
URL: http://svn.freebsd.org/changeset/base/239275

Log:
  Merging of projects/armv6, part 5
  
  - Driver for SMSC LAN95XX and LAN8710A ethernet controllers
  - Driver for LAN8710A PHY
  
  Submitted by: Ben Gray, Damjan Marion, Tim Kientzle

Added:
  head/sys/dev/mii/smscphy.c   (contents, props changed)
  head/sys/dev/usb/net/if_smsc.c   (contents, props changed)
  head/sys/dev/usb/net/if_smscreg.h   (contents, props changed)
  head/sys/modules/usb/smsc/
  head/sys/modules/usb/smsc/Makefile   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/mii/miidevs
  head/sys/dev/mii/smcphy.c
  head/sys/dev/usb/usbdevs
  head/sys/modules/mii/Makefile

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Wed Aug 15 03:49:10 2012        (r239274)
+++ head/sys/conf/files Wed Aug 15 04:03:55 2012        (r239275)
@@ -1612,6 +1612,7 @@ dev/mii/rgephy.c          optional miibus | rgep
 dev/mii/rlphy.c                        optional miibus | rlphy
 dev/mii/rlswitch.c             optional rlswitch
 dev/mii/smcphy.c               optional miibus | smcphy
+dev/mii/smscphy.c              optional miibus | smscphy
 dev/mii/tdkphy.c               optional miibus | tdkphy
 dev/mii/tlphy.c                        optional miibus | tlphy
 dev/mii/truephy.c              optional miibus | truephy
@@ -2118,11 +2119,12 @@ dev/usb/net/if_ipheth.c         optional ipheth
 dev/usb/net/if_kue.c           optional kue
 dev/usb/net/if_mos.c           optional mos
 dev/usb/net/if_rue.c           optional rue
+dev/usb/net/if_smsc.c          optional smsc
 dev/usb/net/if_udav.c          optional udav
 dev/usb/net/if_usie.c          optional usie
 dev/usb/net/ruephy.c           optional rue
 dev/usb/net/usb_ethernet.c     optional aue | axe | cdce | cue | kue | mos | \
-                                        rue | udav
+                                        rue | smsc | udav
 dev/usb/net/uhso.c             optional uhso
 #
 # USB WLAN drivers

Modified: head/sys/dev/mii/miidevs
==============================================================================
--- head/sys/dev/mii/miidevs    Wed Aug 15 03:49:10 2012        (r239274)
+++ head/sys/dev/mii/miidevs    Wed Aug 15 04:03:55 2012        (r239275)
@@ -69,6 +69,7 @@ oui RDC                               0x00d02d        RDC 
Semiconductor
 oui REALTEK                    0x00e04c        RealTek Semicondctor
 oui SEEQ                       0x00a07d        Seeq Technology
 oui SIS                                0x00e006        Silicon Integrated 
Systems
+oui SMC                                0x00800f        SMC
 oui TI                         0x080028        Texas Instruments
 oui TSC                                0x00c039        TDK Semiconductor
 oui VITESSE                    0x0001c1        Vitesse Semiconductor
@@ -325,3 +326,6 @@ model xxVITESSE VSC8641             0x0003 Vitesse 
 
 /* XaQti Corp. PHYs */
 model xxXAQTI XMACII           0x0000 XaQti Corp. XMAC II gigabit interface
+
+/* SMC */
+model SMC LAN8710A             0x000F SMC LAN8710A 10/100 interface

Modified: head/sys/dev/mii/smcphy.c
==============================================================================
--- head/sys/dev/mii/smcphy.c   Wed Aug 15 03:49:10 2012        (r239274)
+++ head/sys/dev/mii/smcphy.c   Wed Aug 15 04:03:55 2012        (r239275)
@@ -26,7 +26,8 @@
 __FBSDID("$FreeBSD$");
 
 /*
- * Driver for the internal PHY on the SMSC LAN91C111.
+ * Driver for the SEEQ 80220 and 84220.
+ * (Originally developed for the internal PHY on the SMSC LAN91C111.)
  */
 
 #include <sys/param.h>

Added: head/sys/dev/mii/smscphy.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/mii/smscphy.c  Wed Aug 15 04:03:55 2012        (r239275)
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2006 Benno Rice.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the SMSC LAN8710A
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include "miidevs.h"
+
+#include "miibus_if.h"
+
+static int     smscphy_probe(device_t);
+static int     smscphy_attach(device_t);
+
+static int     smscphy_service(struct mii_softc *, struct mii_data *, int);
+static void    smscphy_auto(struct mii_softc *, int);
+static void    smscphy_status(struct mii_softc *);
+
+static device_method_t smscphy_methods[] = {
+       /* device interface */
+       DEVMETHOD(device_probe,         smscphy_probe),
+       DEVMETHOD(device_attach,        smscphy_attach),
+       DEVMETHOD(device_detach,        mii_phy_detach),
+       DEVMETHOD(device_shutdown,      bus_generic_shutdown),
+       DEVMETHOD_END
+};
+
+static devclass_t smscphy_devclass;
+
+static driver_t smscphy_driver = {
+       "smscphy",
+       smscphy_methods,
+       sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(smscphy, miibus, smscphy_driver, smscphy_devclass, 0, 0);
+
+static const struct mii_phydesc smscphys[] = {
+       MII_PHY_DESC(SMC, LAN8710A),
+       MII_PHY_END
+};
+
+static const struct mii_phy_funcs smscphy_funcs = {
+       smscphy_service,
+       smscphy_status,
+       mii_phy_reset
+};
+
+static int
+smscphy_probe(device_t dev)
+{
+
+       return (mii_phy_dev_probe(dev, smscphys, BUS_PROBE_DEFAULT));
+}
+
+static int
+smscphy_attach(device_t dev)
+{
+       struct mii_softc *sc;
+       struct mii_attach_args *ma;
+       const struct mii_phy_funcs *mpf;
+
+       sc = device_get_softc(dev);
+       ma = device_get_ivars(dev);
+       mpf = &smscphy_funcs;
+       mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, mpf, 1);
+       mii_phy_setmedia(sc);
+
+       return (0);
+}
+
+static int
+smscphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+        struct ifmedia_entry *ife;
+        int    reg;
+
+       ife = mii->mii_media.ifm_cur;
+
+        switch (cmd) {
+        case MII_POLLSTAT:
+                break;
+
+        case MII_MEDIACHG:
+                /*
+                 * If the interface is not up, don't do anything.
+                 */
+                if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+                        break;
+
+               switch (IFM_SUBTYPE(ife->ifm_media)) {
+               case IFM_AUTO:
+                       smscphy_auto(sc, ife->ifm_media);
+                       break;
+
+               default:
+                       mii_phy_setmedia(sc);
+                       break;
+               }
+
+                break;
+
+        case MII_TICK:
+               if ((mii->mii_ifp->if_flags & IFF_UP) == 0) {
+                       return (0);
+               }
+
+               if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
+                       break;
+               }
+
+               /* I have no idea why BMCR_ISO gets set. */
+               reg = PHY_READ(sc, MII_BMCR);
+               if (reg & BMCR_ISO) {
+                       PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO);
+               }
+
+               reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+               if (reg & BMSR_LINK) {
+                       sc->mii_ticks = 0;
+                       break;
+               }
+
+               if (++sc->mii_ticks <= MII_ANEGTICKS) {
+                       break;
+               }
+
+               sc->mii_ticks = 0;
+               PHY_RESET(sc);
+               smscphy_auto(sc, ife->ifm_media);
+                break;
+        }
+
+        /* Update the media status. */
+        PHY_STATUS(sc);
+
+        /* Callback if something changed. */
+        mii_phy_update(sc, cmd);
+        return (0);
+}
+
+static void
+smscphy_auto(struct mii_softc *sc, int media)
+{
+       uint16_t        anar;
+
+       anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
+       if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+               anar |= ANAR_FC;
+       PHY_WRITE(sc, MII_ANAR, anar);
+       /* Apparently this helps. */
+       anar = PHY_READ(sc, MII_ANAR);
+       PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
+}
+
+static void
+smscphy_status(struct mii_softc *sc)
+{
+       struct mii_data *mii;
+       uint32_t bmcr, bmsr, status;
+
+       mii = sc->mii_pdata;
+       mii->mii_media_status = IFM_AVALID;
+       mii->mii_media_active = IFM_ETHER;
+
+       bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+       if ((bmsr & BMSR_LINK) != 0)
+               mii->mii_media_status |= IFM_ACTIVE;
+
+       bmcr = PHY_READ(sc, MII_BMCR);
+       if ((bmcr & BMCR_ISO) != 0) {
+               mii->mii_media_active |= IFM_NONE;
+               mii->mii_media_status = 0;
+               return;
+       }
+
+       if ((bmcr & BMCR_LOOP) != 0)
+               mii->mii_media_active |= IFM_LOOP;
+
+       if ((bmcr & BMCR_AUTOEN) != 0) {
+               if ((bmsr & BMSR_ACOMP) == 0) {
+                       /* Erg, still trying, I guess... */
+                       mii->mii_media_active |= IFM_NONE;
+                       return;
+               }
+       }
+
+       status = PHY_READ(sc, 0x1F);
+       if (status & 0x0008)
+               mii->mii_media_active |= IFM_100_TX;
+       else
+               mii->mii_media_active |= IFM_10_T;
+       if (status & 0x0010)
+               mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
+       else
+               mii->mii_media_active |= IFM_HDX;
+}

Added: head/sys/dev/usb/net/if_smsc.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/usb/net/if_smsc.c      Wed Aug 15 04:03:55 2012        
(r239275)
@@ -0,0 +1,1756 @@
+/*-
+ * Copyright (c) 2012
+ *     Ben Gray <bg...@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * SMSC LAN9xxx devices (http://www.smsc.com/)
+ * 
+ * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that
+ * support USB 2.0 and 10/100 Mbps Ethernet.
+ *
+ * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter.
+ * The driver only covers the Ethernet part, the standard USB hub driver
+ * supports the hub part.
+ *
+ * This driver is closely modelled on the Linux driver written and copyrighted
+ * by SMSC.
+ *
+ *
+ *
+ *
+ * H/W TCP & UDP Checksum Offloading
+ * ---------------------------------
+ * The chip supports both tx and rx offloading of UDP & TCP checksums, this
+ * feature can be dynamically enabled/disabled.  
+ *
+ * RX checksuming is performed across bytes after the IPv4 header to the end of
+ * the Ethernet frame, this means if the frame is padded with non-zero values
+ * the H/W checksum will be incorrect, however the rx code compensates for 
this.
+ *
+ * TX checksuming is more complicated, the device requires a special header to
+ * be prefixed onto the start of the frame which indicates the start and end
+ * positions of the UDP or TCP frame.  This requires the driver to manually
+ * go through the packet data and decode the headers prior to sending.
+ * On Linux they generally provide cues to the location of the csum and the
+ * area to calculate it over, on FreeBSD we seem to have to do it all 
ourselves,
+ * hence this is not as optimal and therefore h/w tX checksum is currently not
+ * implemented.
+ *
+ */
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/random.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#define        USB_DEBUG_VAR smsc_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/usb_device.h>
+#include <dev/usb/net/usb_ethernet.h>
+#include "if_smscreg.h"
+
+#ifdef USB_DEBUG
+static int smsc_debug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, smsc, CTLFLAG_RW, 0, "USB smsc");
+SYSCTL_INT(_hw_usb_smsc, OID_AUTO, debug, CTLFLAG_RW, &smsc_debug, 0,
+    "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb_device_id smsc_devs[] = {
+#define        SMSC_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, 
i) }
+       SMSC_DEV(LAN9514_ETH, 0),
+#undef SMSC_DEV
+};
+
+
+#ifdef USB_DEBUG
+#define smsc_dbg_printf(sc, fmt, args...) \
+       do { \
+               if (smsc_debug > 0) \
+                       device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, 
##args); \
+       } while(0)
+#else
+#define smsc_dbg_printf(sc, fmt, args...)
+#endif
+
+#define smsc_warn_printf(sc, fmt, args...) \
+       device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args)
+
+#define smsc_err_printf(sc, fmt, args...) \
+       device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args)
+       
+
+#define ETHER_IS_ZERO(addr) \
+       (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]))
+       
+#define ETHER_IS_VALID(addr) \
+       (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr))
+       
+static device_probe_t smsc_probe;
+static device_attach_t smsc_attach;
+static device_detach_t smsc_detach;
+
+static usb_callback_t smsc_bulk_read_callback;
+static usb_callback_t smsc_bulk_write_callback;
+
+static miibus_readreg_t smsc_miibus_readreg;
+static miibus_writereg_t smsc_miibus_writereg;
+static miibus_statchg_t smsc_miibus_statchg;
+
+#if __FreeBSD_version > 1000000
+static int smsc_attach_post_sub(struct usb_ether *ue);
+#endif
+static uether_fn_t smsc_attach_post;
+static uether_fn_t smsc_init;
+static uether_fn_t smsc_stop;
+static uether_fn_t smsc_start;
+static uether_fn_t smsc_tick;
+static uether_fn_t smsc_setmulti;
+static uether_fn_t smsc_setpromisc;
+
+static int     smsc_ifmedia_upd(struct ifnet *);
+static void    smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static int smsc_chip_init(struct smsc_softc *sc);
+static int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
+
+static const struct usb_config smsc_config[SMSC_N_TRANSFER] = {
+
+       [SMSC_BULK_DT_WR] = {
+               .type = UE_BULK,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_OUT,
+               .frames = 16,
+               .bufsize = 16 * (MCLBYTES + 16),
+               .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+               .callback = smsc_bulk_write_callback,
+               .timeout = 10000,       /* 10 seconds */
+       },
+
+       [SMSC_BULK_DT_RD] = {
+               .type = UE_BULK,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_IN,
+               .bufsize = 20480,       /* bytes */
+               .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+               .callback = smsc_bulk_read_callback,
+               .timeout = 0,   /* no timeout */
+       },
+
+       /* The SMSC chip supports an interrupt endpoints, however they aren't
+        * needed as we poll on the MII status.
+        */
+};
+
+static const struct usb_ether_methods smsc_ue_methods = {
+       .ue_attach_post = smsc_attach_post,
+#if __FreeBSD_version > 1000000
+       .ue_attach_post_sub = smsc_attach_post_sub,
+#endif
+       .ue_start = smsc_start,
+       .ue_ioctl = smsc_ioctl,
+       .ue_init = smsc_init,
+       .ue_stop = smsc_stop,
+       .ue_tick = smsc_tick,
+       .ue_setmulti = smsc_setmulti,
+       .ue_setpromisc = smsc_setpromisc,
+       .ue_mii_upd = smsc_ifmedia_upd,
+       .ue_mii_sts = smsc_ifmedia_sts,
+};
+
+/**
+ *     smsc_read_reg - Reads a 32-bit register on the device
+ *     @sc: driver soft context
+ *     @off: offset of the register
+ *     @data: pointer a value that will be populated with the register value
+ *     
+ *     LOCKING:
+ *     The device lock must be held before calling this function.
+ *
+ *     RETURNS:
+ *     0 on success, a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data)
+{
+       struct usb_device_request req;
+       uint32_t buf;
+       usb_error_t err;
+
+       SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+       req.bmRequestType = UT_READ_VENDOR_DEVICE;
+       req.bRequest = SMSC_UR_READ_REG;
+       USETW(req.wValue, 0);
+       USETW(req.wIndex, off);
+       USETW(req.wLength, 4);
+
+       err = uether_do_request(&sc->sc_ue, &req, &buf, 1000);
+       if (err != 0)
+               smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off);
+
+       *data = le32toh(buf);
+       
+       return (err);
+}
+
+/**
+ *     smsc_write_reg - Writes a 32-bit register on the device
+ *     @sc: driver soft context
+ *     @off: offset of the register
+ *     @data: the 32-bit value to write into the register
+ *     
+ *     LOCKING:
+ *     The device lock must be held before calling this function.
+ *
+ *     RETURNS:
+ *     0 on success, a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data)
+{
+       struct usb_device_request req;
+       uint32_t buf;
+       usb_error_t err;
+
+       SMSC_LOCK_ASSERT(sc, MA_OWNED);
+       
+       buf = htole32(data);
+
+       req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+       req.bRequest = SMSC_UR_WRITE_REG;
+       USETW(req.wValue, 0);
+       USETW(req.wIndex, off);
+       USETW(req.wLength, 4);
+
+       err = uether_do_request(&sc->sc_ue, &req, &buf, 1000);
+       if (err != 0)
+               smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off);
+
+       return (err);
+}
+
+/**
+ *     smsc_wait_for_bits - Polls on a register value until bits are cleared
+ *     @sc: soft context
+ *     @reg: offset of the register
+ *     @bits: if the bits are clear the function returns
+ *
+ *     LOCKING:
+ *     The device lock must be held before calling this function.
+ *
+ *     RETURNS:
+ *     0 on success, or a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits)
+{
+       usb_ticks_t start_ticks;
+       usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
+       uint32_t val;
+       int err;
+       
+       SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+       start_ticks = (usb_ticks_t)ticks;
+       do {
+               if ((err = smsc_read_reg(sc, reg, &val)) != 0)
+                       return (err);
+               if (!(val & bits))
+                       return (0);
+               
+               uether_pause(&sc->sc_ue, hz / 100);
+       } while ((ticks - start_ticks) < max_ticks);
+
+       return (USB_ERR_TIMEOUT);
+}
+
+/**
+ *     smsc_eeprom_read - Reads the attached EEPROM
+ *     @sc: soft context
+ *     @off: the eeprom address offset
+ *     @buf: stores the bytes
+ *     @buflen: the number of bytes to read
+ *
+ *     Simply reads bytes from an attached eeprom.
+ *
+ *     LOCKING:
+ *     The function takes and releases the device lock if it is not already 
held.
+ *
+ *     RETURNS:
+ *     0 on success, or a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_eeprom_read(struct smsc_softc *sc, uint16_t off, uint8_t *buf, uint16_t 
buflen)
+{
+       usb_ticks_t start_ticks;
+       usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
+       int err;
+       int locked;
+       uint32_t val;
+       uint16_t i;
+
+       locked = mtx_owned(&sc->sc_mtx);
+       if (!locked)
+               SMSC_LOCK(sc);
+
+       err = smsc_wait_for_bits(sc, SMSC_EEPROM_CMD, SMSC_EEPROM_CMD_BUSY);
+       if (err != 0) {
+               smsc_warn_printf(sc, "eeprom busy, failed to read data\n");
+               goto done;
+       }
+
+       /* start reading the bytes, one at a time */
+       for (i = 0; i < buflen; i++) {
+       
+               val = SMSC_EEPROM_CMD_BUSY | (SMSC_EEPROM_CMD_ADDR_MASK & (off 
+ i));
+               if ((err = smsc_write_reg(sc, SMSC_EEPROM_CMD, val)) != 0)
+                       goto done;
+               
+               start_ticks = (usb_ticks_t)ticks;
+               do {
+                       if ((err = smsc_read_reg(sc, SMSC_EEPROM_CMD, &val)) != 
0)
+                               goto done;
+                       if (!(val & SMSC_EEPROM_CMD_BUSY) || (val & 
SMSC_EEPROM_CMD_TIMEOUT))
+                               break;
+
+                       uether_pause(&sc->sc_ue, hz / 100);
+               } while ((ticks - start_ticks) < max_ticks);
+               
+               if (val & (SMSC_EEPROM_CMD_BUSY | SMSC_EEPROM_CMD_TIMEOUT)) {
+                       smsc_warn_printf(sc, "eeprom command failed\n");
+                       err = USB_ERR_IOERROR;
+                       break;
+               }
+                       
+               if ((err = smsc_read_reg(sc, SMSC_EEPROM_DATA, &val)) != 0)
+                       goto done;
+
+               buf[i] = (val & 0xff);
+       }
+       
+done:
+       if (!locked)
+               SMSC_UNLOCK(sc);
+
+       return (err);
+}
+
+/**
+ *     smsc_miibus_readreg - Reads a MII/MDIO register
+ *     @dev: usb ether device
+ *     @phy: the number of phy reading from
+ *     @reg: the register address
+ *
+ *     Attempts to read a phy register over the MII bus.
+ *
+ *     LOCKING:
+ *     Takes and releases the device mutex lock if not already held.
+ *
+ *     RETURNS:
+ *     Returns the 16-bits read from the MII register, if this function fails 0
+ *     is returned.
+ */
+static int
+smsc_miibus_readreg(device_t dev, int phy, int reg)
+{
+       struct smsc_softc *sc = device_get_softc(dev);
+       int locked;
+       uint32_t addr;
+       uint32_t val = 0;
+
+       locked = mtx_owned(&sc->sc_mtx);
+       if (!locked)
+               SMSC_LOCK(sc);
+
+       if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
+               smsc_warn_printf(sc, "MII is busy\n");
+               goto done;
+       }
+
+       addr = (phy << 11) | (reg << 6) | SMSC_MII_READ;
+       smsc_write_reg(sc, SMSC_MII_ADDR, addr);
+
+       if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
+               smsc_warn_printf(sc, "MII read timeout\n");
+
+       smsc_read_reg(sc, SMSC_MII_DATA, &val);
+       val = le32toh(val);
+       
+done:
+       if (!locked)
+               SMSC_UNLOCK(sc);
+
+       return (val & 0xFFFF);
+}
+
+/**
+ *     smsc_miibus_writereg - Writes a MII/MDIO register
+ *     @dev: usb ether device
+ *     @phy: the number of phy writing to
+ *     @reg: the register address
+ *     @val: the value to write
+ *
+ *     Attempts to write a phy register over the MII bus.
+ *
+ *     LOCKING:
+ *     Takes and releases the device mutex lock if not already held.
+ *
+ *     RETURNS:
+ *     Always returns 0 regardless of success or failure.
+ */
+static int
+smsc_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+       struct smsc_softc *sc = device_get_softc(dev);
+       int locked;
+       uint32_t addr;
+
+       if (sc->sc_phyno != phy)
+               return (0);
+
+       locked = mtx_owned(&sc->sc_mtx);
+       if (!locked)
+               SMSC_LOCK(sc);
+
+       if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
+               smsc_warn_printf(sc, "MII is busy\n");
+               goto done;
+       }
+
+       val = htole32(val);
+       smsc_write_reg(sc, SMSC_MII_DATA, val);
+
+       addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE;
+       smsc_write_reg(sc, SMSC_MII_ADDR, addr);
+
+       if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
+               smsc_warn_printf(sc, "MII write timeout\n");
+
+done:
+       if (!locked)
+               SMSC_UNLOCK(sc);
+       return (0);
+}
+
+
+
+/**
+ *     smsc_miibus_statchg - Called to detect phy status change
+ *     @dev: usb ether device
+ *
+ *     This function is called periodically by the system to poll for status
+ *     changes of the link.
+ *
+ *     LOCKING:
+ *     Takes and releases the device mutex lock if not already held.
+ */
+static void
+smsc_miibus_statchg(device_t dev)
+{
+       struct smsc_softc *sc = device_get_softc(dev);
+       struct mii_data *mii = uether_getmii(&sc->sc_ue);
+       struct ifnet *ifp;
+       int locked;
+       int err;
+       uint32_t flow;
+       uint32_t afc_cfg;
+
+       locked = mtx_owned(&sc->sc_mtx);
+       if (!locked)
+               SMSC_LOCK(sc);
+
+       ifp = uether_getifp(&sc->sc_ue);
+       if (mii == NULL || ifp == NULL ||
+           (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+               goto done;
+
+       /* Use the MII status to determine link status */
+       sc->sc_flags &= ~SMSC_FLAG_LINK;
+       if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+           (IFM_ACTIVE | IFM_AVALID)) {
+               switch (IFM_SUBTYPE(mii->mii_media_active)) {
+                       case IFM_10_T:
+                       case IFM_100_TX:
+                               sc->sc_flags |= SMSC_FLAG_LINK;
+                               break;
+                       case IFM_1000_T:
+                               /* Gigabit ethernet not supported by chipset */
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       /* Lost link, do nothing. */
+       if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
+               smsc_dbg_printf(sc, "link flag not set\n");
+               goto done;
+       }
+       
+       err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg);
+       if (err) {
+               smsc_warn_printf(sc, "failed to read initial AFC_CFG, error 
%d\n", err);
+               goto done;
+       }
+       
+       /* Enable/disable full duplex operation and TX/RX pause */
+       if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
+               smsc_dbg_printf(sc, "full duplex operation\n");
+               sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN;
+               sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX;
+
+               if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+                       flow = 0xffff0002;
+               else
+                       flow = 0;
+                       
+               if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+                       afc_cfg |= 0xf;
+               else
+                       afc_cfg &= ~0xf;
+               
+       } else {
+               smsc_dbg_printf(sc, "half duplex operation\n");
+               sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX;
+               sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN;
+               
+               flow = 0;
+               afc_cfg |= 0xf;
+       }
+
+       err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
+       err += smsc_write_reg(sc, SMSC_FLOW, flow);
+       err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg);
+       if (err)
+               smsc_warn_printf(sc, "media change failed, error %d\n", err);
+       
+done:
+       if (!locked)
+               SMSC_UNLOCK(sc);
+}
+
+/**
+ *     smsc_ifmedia_upd - Set media options
+ *     @ifp: interface pointer
+ *
+ *     Basically boilerplate code that simply calls the mii functions to set 
the
+ *     media options.
+ *
+ *     LOCKING:
+ *     The device lock must be held before this function is called.
+ *
+ *     RETURNS:
+ *     Returns 0 on success or a negative error code.
+ */
+static int
+smsc_ifmedia_upd(struct ifnet *ifp)
+{
+       struct smsc_softc *sc = ifp->if_softc;
+       struct mii_data *mii = uether_getmii(&sc->sc_ue);
+       int err;
+
+       SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+       if (mii->mii_instance) {
+               struct mii_softc *miisc;
+
+               LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+                       mii_phy_reset(miisc);
+       }
+       err = mii_mediachg(mii);
+       return (err);
+}
+
+/**
+ *     smsc_ifmedia_sts - Report current media status
+ *     @ifp: inet interface pointer
+ *     @ifmr: interface media request
+ *
+ *     Basically boilerplate code that simply calls the mii functions to get 
the
+ *     media status.
+ *
+ *     LOCKING:
+ *     Internally takes and releases the device lock.
+ */
+static void
+smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+       struct smsc_softc *sc = ifp->if_softc;
+       struct mii_data *mii = uether_getmii(&sc->sc_ue);
+
+       SMSC_LOCK(sc);
+       
+       mii_pollstat(mii);
+       
+       SMSC_UNLOCK(sc);
+       
+       ifmr->ifm_active = mii->mii_media_active;
+       ifmr->ifm_status = mii->mii_media_status;
+}
+
+/**
+ *     smsc_hash - Calculate the hash of a mac address
+ *     @addr: The mac address to calculate the hash on
+ *
+ *     This function is used when configuring a range of m'cast mac addresses 
to
+ *     filter on.  The hash of the mac address is put in the device's mac hash
+ *     table.
+ *
+ *     RETURNS:
+ *     Returns a value from 0-63 value which is the hash of the mac address.
+ */
+static inline uint32_t
+smsc_hash(uint8_t addr[ETHER_ADDR_LEN])
+{
+       return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f;
+}
+
+/**
+ *     smsc_setmulti - Setup multicast
+ *     @ue: usb ethernet device context
+ *
+ *     Tells the device to either accept frames with a multicast mac address, a
+ *     select group of m'cast mac addresses or just the devices mac address.
+ *
+ *     LOCKING:
+ *     Should be called with the SMSC lock held.
+ */
+static void
+smsc_setmulti(struct usb_ether *ue)
+{
+       struct smsc_softc *sc = uether_getsc(ue);
+       struct ifnet *ifp = uether_getifp(ue);
+       struct ifmultiaddr *ifma;
+       uint32_t hashtbl[2] = { 0, 0 };
+       uint32_t hash;
+
+       SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+       if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+               smsc_dbg_printf(sc, "receive all multicast enabled\n");
+               sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
+               sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT;
+               
+       } else {
+               /* Take the lock of the mac address list before hashing each of 
them */
+               if_maddr_rlock(ifp);
+
+               if (!TAILQ_EMPTY(&ifp->if_multiaddrs)) {
+                       /* We are filtering on a set of address so calculate 
hashes of each
+                        * of the address and set the corresponding bits in the 
register.
+                        */
+                       sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT;
+                       sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | 
SMSC_MAC_CSR_MCPAS);
+               
+                       TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+                               if (ifma->ifma_addr->sa_family != AF_LINK)
+                                       continue;
+
+                               hash = smsc_hash(LLADDR((struct sockaddr_dl 
*)ifma->ifma_addr));
+                               hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
+                       }
+               } else {
+                       /* Only receive packets with destination set to our mac 
address */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to