From owner-svn-src-all@FreeBSD.ORG Wed Aug 15 04:03:56 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 39F93106564A; Wed, 15 Aug 2012 04:03:55 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id DBB7A8FC16; Wed, 15 Aug 2012 04:03:55 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q7F43tAi097613; Wed, 15 Aug 2012 04:03:55 GMT (envelope-from gonzo@svn.freebsd.org) Received: (from gonzo@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q7F43t56097606; Wed, 15 Aug 2012 04:03:55 GMT (envelope-from gonzo@svn.freebsd.org) Message-Id: <201208150403.q7F43t56097606@svn.freebsd.org> From: Oleksandr Tymoshenko Date: Wed, 15 Aug 2012 04:03:55 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r239275 - in head/sys: conf dev/mii dev/usb dev/usb/net modules/mii modules/usb/smsc X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Aug 2012 04:03:56 -0000 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 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 +__FBSDID("$FreeBSD$"); + +/* + * Driver for the SMSC LAN8710A + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#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 . + * 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 +__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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR smsc_debug +#include +#include + +#include +#include +#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 ***