Date: Mon, 28 May 2018 23:00:17 +0200 From: "Ronald Klop" <ronald-lists@klop.ws> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org, "Ed Maste" <emaste@freebsd.org> Subject: Re: svn commit: r333713 - in head/sys: dev/usb/net modules/usb/muge Message-ID: <op.zjqsmrwikndu52@klop.ws> In-Reply-To: <201805171405.w4HE50pg052669@repo.freebsd.org> References: <201805171405.w4HE50pg052669@repo.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
The addition of this driver made me finally buy a RPI3B+. Thanks a lot! Regards, Ronald. On Thu, 17 May 2018 16:05:00 +0200, Ed Maste <emaste@freebsd.org> wrote: > Author: emaste > Date: Thu May 17 14:04:59 2018 > New Revision: 333713 > URL: https://svnweb.freebsd.org/changeset/base/333713 > > Log: > Add driver for Microchip LAN78xx USB3-GigE controller > This driver supports two Microchip USB-Ethernet controllers: > LAN7800 USB 3.1 to 10/100/1000 Mbps Ethernet > LAN7515 USB 2 to 10/100/1000 Mbps Ethernet with built-in USB hub > The LAN7515 is the Ethernet controller on the Raspberry Pi 3B+. > At present there is no datasheet for the LAN7515, but it is effectively > a USB 2 hub combined with a LAN7800 controller. A comprehensive > LAN7800 > datasheet is at http://www.microchip.com/wwwproducts/en/LAN7800. > This driver is based on the structure of the smsc(4) driver which > supports Microchip/SMSC's LAN95xx family. (Microchip acquired SMSC > in May 2012.) The Linux lan78xx driver served as a reference for some > functionality and registers. > The 'muge' driver name comes from "Microchip USB Gigabit Ethernet". > I made some style adjustments and minor edits to Arshan's submission. > It will be connected to the build after additional review and testing. > Thanks to Microchip for providing a number of Evaluation Boards (EVBs) > for development and testing. > Submitted by: Arshan Khanifar > Reviewed by: hselasky (earlier) > Sponsored by: The FreeBSD Foundation > Differential Revision: https://reviews.freebsd.org/D15168 > > Added: > head/sys/dev/usb/net/if_muge.c (contents, props changed) > head/sys/dev/usb/net/if_mugereg.h (contents, props changed) > head/sys/modules/usb/muge/ > head/sys/modules/usb/muge/Makefile (contents, props changed) > > Added: head/sys/dev/usb/net/if_muge.c > ============================================================================== > --- /dev/null 00:00:00 1970 (empty, because file is newly added) > +++ head/sys/dev/usb/net/if_muge.c Thu May 17 14:04:59 2018 (r333713) > @@ -0,0 +1,2199 @@ > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * Copyright (C) 2012 Ben Gray <bgray@freebsd.org>. > + * Copyright (C) 2018 The FreeBSD Foundation. > + * > + * This software was developed by Arshan Khanifar > <arshankhanifar@gmail.com> > + * under sponsorship from the FreeBSD Foundation. > + * > + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. > + * > + * $FreeBSD$ > + */ > + > +#include <sys/cdefs.h> > +__FBSDID("$FreeBSD$"); > + > +/* > + * USB-To-Ethernet adapter driver for Microchip's LAN78XX and related > families. > + * > + * USB 3.1 to 10/100/1000 Mbps Ethernet > + * LAN7800 http://www.microchip.com/wwwproducts/en/LAN7800 > + * > + * USB 2 to 10/100/1000 Mbps Ethernet with built-in USB hub > + * LAN7515 (no datasheet available, but probes and functions as LAN7800) > + * > + * This driver is based on the if_smsc driver, with lan78xx-specific > + * functionality modelled on Microchip's Linux lan78xx driver. > + * > + * UNIMPLEMENTED FEATURES > + * ------------------ > + * A number of features supported by the lan78xx are not yet > implemented in > + * this driver: > + * > + * 1. RX/TX checksum offloading: Nothing has been implemented yet for > + * TX checksumming. RX checksumming works with ICMP messages, but is > broken > + * for TCP/UDP packets. > + * 2. Direct address translation filtering: Implemented but untested. > + * 3. VLAN tag removal. > + * 4. Reading MAC address from the device tree: Specific to the RPi 3B+. > + * Currently, the driver assigns a random MAC address itself. > + * 5. Support for USB interrupt endpoints. > + * 6. Latency Tolerance Messaging (LTM) support. > + * 7. TCP LSO support. > + * > + */ > + > +#include <sys/param.h> > +#include <sys/bus.h> > +#include <sys/callout.h> > +#include <sys/condvar.h> > +#include <sys/kernel.h> > +#include <sys/lock.h> > +#include <sys/malloc.h> > +#include <sys/module.h> > +#include <sys/mutex.h> > +#include <sys/priv.h> > +#include <sys/queue.h> > +#include <sys/random.h> > +#include <sys/socket.h> > +#include <sys/stddef.h> > +#include <sys/stdint.h> > +#include <sys/sx.h> > +#include <sys/sysctl.h> > +#include <sys/systm.h> > +#include <sys/unistd.h> > + > +#include <net/if.h> > +#include <net/if_var.h> > + > +#include <netinet/in.h> > +#include <netinet/ip.h> > + > +#include "opt_platform.h" > + > +#include <dev/usb/usb.h> > +#include <dev/usb/usbdi.h> > +#include <dev/usb/usbdi_util.h> > +#include "usbdevs.h" > + > +#define USB_DEBUG_VAR lan78xx_debug > +#include <dev/usb/usb_debug.h> > +#include <dev/usb/usb_process.h> > + > +#include <dev/usb/net/usb_ethernet.h> > + > +#include <dev/usb/net/if_mugereg.h> > + > +#ifdef USB_DEBUG > +static int muge_debug = 0; > + > +SYSCTL_NODE(_hw_usb, OID_AUTO, muge, CTLFLAG_RW, 0, > + "Microchip LAN78xx USB-GigE"); > +SYSCTL_INT(_hw_usb_muge, OID_AUTO, debug, CTLFLAG_RWTUN, &muge_debug, 0, > + "Debug level"); > +#endif > + > +#define MUGE_DEFAULT_RX_CSUM_ENABLE (false) > +#define MUGE_DEFAULT_TX_CSUM_ENABLE (false) > +#define MUGE_DEFAULT_TSO_CSUM_ENABLE (false) > + > +/* Supported Vendor and Product IDs. */ > +static const struct usb_device_id lan78xx_devs[] = { > +#define MUGE_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, > i) } > + MUGE_DEV(LAN7800_ETH, 0), > +#undef MUGE_DEV > +}; > + > +#ifdef USB_DEBUG > +#define lan78xx_dbg_printf(sc, fmt, args...) \ > +do { \ > + if (muge_debug > 0) \ > + device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \ > +} while(0) > +#else > +#define muge_dbg_printf(sc, fmt, args...) do { } while (0) > +#endif > + > +#define muge_warn_printf(sc, fmt, args...) \ > + device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args) > + > +#define muge_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)) > + > +/* USB endpoints. */ > + > +enum { > + MUGE_BULK_DT_RD, > + MUGE_BULK_DT_WR, > + /* > + * the device does support interrupt endpoints, > + * but they're not needed as we poll on MII status. > + * MUGE_INTR_DT_WR, > + * MUGE_INTR_DT_RD, > + */ > + MUGE_N_TRANSFER, > +}; > + > +struct muge_softc { > + struct usb_ether sc_ue; > + struct mtx sc_mtx; > + struct usb_xfer *sc_xfer[MUGE_N_TRANSFER]; > + int sc_phyno; > + > + /* Settings for the mac control (MAC_CSR) register. */ > + uint32_t sc_rfe_ctl; > + uint32_t sc_mdix_ctl; > + uint32_t sc_rev_id; > + uint32_t sc_mchash_table[DP_SEL_VHF_HASH_LEN]; > + uint32_t sc_pfilter_table[MUGE_NUM_PFILTER_ADDRS_][2]; > + > + uint32_t sc_flags; > +#define MUGE_FLAG_LINK 0x0001 > +}; > + > +#define MUGE_IFACE_IDX 0 > + > +#define MUGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) > +#define MUGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) > +#define MUGE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) > + > + > +static device_probe_t muge_probe; > +static device_attach_t muge_attach; > +static device_detach_t muge_detach; > + > +static usb_callback_t muge_bulk_read_callback; > +static usb_callback_t muge_bulk_write_callback; > + > +static miibus_readreg_t lan78xx_miibus_readreg; > +static miibus_writereg_t lan78xx_miibus_writereg; > +static miibus_statchg_t lan78xx_miibus_statchg; > + > +static int muge_attach_post_sub(struct usb_ether *ue); > +static uether_fn_t muge_attach_post; > +static uether_fn_t muge_init; > +static uether_fn_t muge_stop; > +static uether_fn_t muge_start; > +static uether_fn_t muge_tick; > +static uether_fn_t muge_setmulti; > +static uether_fn_t muge_setpromisc; > + > +static int muge_ifmedia_upd(struct ifnet *); > +static void muge_ifmedia_sts(struct ifnet *, struct ifmediareq *); > + > +static int lan78xx_chip_init(struct muge_softc *sc); > +static int muge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); > + > +static const struct usb_config muge_config[MUGE_N_TRANSFER] = { > + > + [MUGE_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 = muge_bulk_write_callback, > + .timeout = 10000, /* 10 seconds */ > + }, > + > + [MUGE_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 = muge_bulk_read_callback, > + .timeout = 0, /* no timeout */ > + }, > + /* > + * The chip supports interrupt endpoints, however they aren't > + * needed as we poll on the MII status. > + */ > +}; > + > +static const struct usb_ether_methods muge_ue_methods = { > + .ue_attach_post = muge_attach_post, > + .ue_attach_post_sub = muge_attach_post_sub, > + .ue_start = muge_start, > + .ue_ioctl = muge_ioctl, > + .ue_init = muge_init, > + .ue_stop = muge_stop, > + .ue_tick = muge_tick, > + .ue_setmulti = muge_setmulti, > + .ue_setpromisc = muge_setpromisc, > + .ue_mii_upd = muge_ifmedia_upd, > + .ue_mii_sts = muge_ifmedia_sts, > +}; > + > +/** > + * lan78xx_read_reg - Read 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 > +lan78xx_read_reg(struct muge_softc *sc, uint32_t off, uint32_t *data) > +{ > + struct usb_device_request req; > + uint32_t buf; > + usb_error_t err; > + > + MUGE_LOCK_ASSERT(sc, MA_OWNED); > + > + req.bmRequestType = UT_READ_VENDOR_DEVICE; > + req.bRequest = UVR_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) > + muge_warn_printf(sc, "Failed to read register 0x%0x\n", off); > + *data = le32toh(buf); > + return (err); > +} > + > +/** > + * lan78xx_write_reg - Write 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 > +lan78xx_write_reg(struct muge_softc *sc, uint32_t off, uint32_t data) > +{ > + struct usb_device_request req; > + uint32_t buf; > + usb_error_t err; > + > + MUGE_LOCK_ASSERT(sc, MA_OWNED); > + > + buf = htole32(data); > + > + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; > + req.bRequest = UVR_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) > + muge_warn_printf(sc, "Failed to write register 0x%0x\n", off); > + return (err); > +} > + > +/** > + * lan78xx_wait_for_bits - Poll 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 > +lan78xx_wait_for_bits(struct muge_softc *sc, uint32_t reg, uint32_t > bits) > +{ > + usb_ticks_t start_ticks; > + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); > + uint32_t val; > + int err; > + > + MUGE_LOCK_ASSERT(sc, MA_OWNED); > + > + start_ticks = (usb_ticks_t)ticks; > + do { > + if ((err = lan78xx_read_reg(sc, reg, &val)) != 0) > + return (err); > + if (!(val & bits)) > + return (0); > + uether_pause(&sc->sc_ue, hz / 100); > + } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); > + > + return (USB_ERR_TIMEOUT); > +} > + > +/** > + * lan78xx_eeprom_read_raw - Read 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 not already held. > + * > + * RETURNS: > + * 0 on success, or a USB_ERR_?? error code on failure. > + */ > +static int > +lan78xx_eeprom_read_raw(struct muge_softc *sc, uint16_t off, uint8_t > *buf, > + uint16_t buflen) > +{ > + usb_ticks_t start_ticks; > + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); > + int err, locked; > + uint32_t val, saved; > + uint16_t i; > + > + locked = mtx_owned(&sc->sc_mtx); /* XXX */ > + if (!locked) > + MUGE_LOCK(sc); > + > + err = lan78xx_read_reg(sc, HW_CFG, &val); > + saved = val; > + > + val &= ~(HW_CFG_LEDO_EN_ | HW_CFG_LED1_EN_); > + err = lan78xx_write_reg(sc, HW_CFG, val); > + > + err = lan78xx_wait_for_bits(sc, E2P_CMD, E2P_CMD_BUSY_); > + if (err != 0) { > + muge_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 = E2P_CMD_BUSY_ | E2P_CMD_READ_; > + val |= (E2P_CMD_ADDR_MASK_ & (off + i)); > + if ((err = lan78xx_write_reg(sc, E2P_CMD, val)) != 0) > + goto done; > + > + start_ticks = (usb_ticks_t)ticks; > + do { > + if ((err = lan78xx_read_reg(sc, E2P_CMD, &val)) != 0) > + goto done; > + if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) > + break; > + > + uether_pause(&sc->sc_ue, hz / 100); > + } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); > + > + if (val & (E2P_CMD_BUSY_ | E2P_CMD_TIMEOUT_)) { > + muge_warn_printf(sc, "eeprom command failed\n"); > + err = USB_ERR_IOERROR; > + break; > + } > + > + if ((err = lan78xx_read_reg(sc, E2P_DATA, &val)) != 0) > + goto done; > + > + buf[i] = (val & 0xff); > + } > + > +done: > + if (!locked) > + MUGE_UNLOCK(sc); > + lan78xx_write_reg(sc, HW_CFG, saved); > + return (err); > +} > + > +/** > + * lan78xx_eeprom_read - Read EEPROM and confirm it is programmed > + * @sc: soft context > + * @off: the eeprom address offset > + * @buf: stores the bytes > + * @buflen: the number of bytes to read > + * > + * RETURNS: > + * 0 on success, or a USB_ERR_?? error code on failure. > + */ > +static int > +lan78xx_eeprom_read(struct muge_softc *sc, uint16_t off, uint8_t *buf, > + uint16_t buflen) > +{ > + uint8_t sig; > + int ret; > + > + ret = lan78xx_eeprom_read_raw(sc, E2P_INDICATOR_OFFSET, &sig, 1); > + if ((ret == 0) && (sig == E2P_INDICATOR)) { > + ret = lan78xx_eeprom_read_raw(sc, off, buf, buflen); > + muge_dbg_printf(sc, "EEPROM present\n"); > + } else { > + ret = -EINVAL; > + muge_dbg_printf(sc, "EEPROM not present\n"); > + } > + return ret; > +} > + > +/** > + * lan78xx_otp_read_raw > + * @sc: soft context > + * @off: the otp address offset > + * @buf: stores the bytes > + * @buflen: the number of bytes to read > + * > + * Simply reads bytes from the OTP. > + * > + * LOCKING: > + * The function takes and releases the device lock if not already held. > + * > + * RETURNS: > + * 0 on success, or a USB_ERR_?? error code on failure. > + * > + */ > +static int > +lan78xx_otp_read_raw(struct muge_softc *sc, uint16_t off, uint8_t *buf, > + uint16_t buflen) > +{ > + int locked, err; > + uint32_t val; > + uint16_t i; > + locked = mtx_owned(&sc->sc_mtx); > + if (!locked) > + MUGE_LOCK(sc); > + > + err = lan78xx_read_reg(sc, OTP_PWR_DN, &val); > + > + /* checking if bit is set */ > + if (val & OTP_PWR_DN_PWRDN_N) { > + /* clearing it, then waiting for it to be cleared */ > + lan78xx_write_reg(sc, OTP_PWR_DN, 0); > + err = lan78xx_wait_for_bits(sc, OTP_PWR_DN, OTP_PWR_DN_PWRDN_N); > + if (err != 0) { > + muge_warn_printf(sc, "OTP off? failed to read data\n"); > + goto done; > + } > + } > + /* start reading the bytes, one at a time */ > + for (i = 0; i < buflen; i++) { > + err = lan78xx_write_reg(sc, OTP_ADDR1, > + ((off + i) >> 8) & OTP_ADDR1_15_11); > + err = lan78xx_write_reg(sc, OTP_ADDR2, > + ((off + i) & OTP_ADDR2_10_3)); > + err = lan78xx_write_reg(sc, OTP_FUNC_CMD, OTP_FUNC_CMD_READ_); > + err = lan78xx_write_reg(sc, OTP_CMD_GO, OTP_CMD_GO_GO_); > + > + err = lan78xx_wait_for_bits(sc, OTP_STATUS, OTP_STATUS_BUSY_); > + if (err != 0) { > + muge_warn_printf(sc, "OTP busy failed to read data\n"); > + goto done; > + } > + > + if ((err = lan78xx_read_reg(sc, OTP_RD_DATA, &val)) != 0) > + goto done; > + > + buf[i] = (uint8_t)(val & 0xff); > + } > + > +done: > + if (!locked) > + MUGE_UNLOCK(sc); > + return (err); > +} > + > +/** > + * lan78xx_otp_read > + * @sc: soft context > + * @off: the otp address offset > + * @buf: stores the bytes > + * @buflen: the number of bytes to read > + * > + * Simply reads bytes from the otp. > + * > + * LOCKING: > + * The function takes and releases device lock if it is not already > held. > + * > + * RETURNS: > + * 0 on success, or a USB_ERR_?? error code on failure. > + */ > +static int > +lan78xx_otp_read(struct muge_softc *sc, uint16_t off, uint8_t *buf, > + uint16_t buflen) > +{ > + uint8_t sig; > + int err; > + > + err = lan78xx_otp_read_raw(sc, OTP_INDICATOR_OFFSET, &sig, 1); > + if (err == 0) { > + if (sig == OTP_INDICATOR_1) { > + } else if (sig == OTP_INDICATOR_2) { > + off += 0x100; > + } else { > + err = -EINVAL; > + } > + if(!err) > + err = lan78xx_otp_read_raw(sc, off, buf, buflen); > + } > + return err; > +} > + > +/** > + * lan78xx_setmacaddress - Set the mac address in the device > + * @sc: driver soft context > + * @addr: pointer to array contain at least 6 bytes of the mac > + * > + * LOCKING: > + * Should be called with the MUGE lock held. > + * > + * RETURNS: > + * Returns 0 on success or a negative error code. > + */ > +static int > +lan78xx_setmacaddress(struct muge_softc *sc, const uint8_t *addr) > +{ > + int err; > + uint32_t val; > + > + muge_dbg_printf(sc, > + "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n", > + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); > + > + MUGE_LOCK_ASSERT(sc, MA_OWNED); > + > + val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; > + if ((err = lan78xx_write_reg(sc, RX_ADDRL, val)) != 0) > + goto done; > + > + val = (addr[5] << 8) | addr[4]; > + err = lan78xx_write_reg(sc, RX_ADDRH, val); > + > +done: > + return (err); > +} > + > +/** > + * lan78xx_set_rx_max_frame_length > + * @sc: driver soft context > + * @size: pointer to array contain at least 6 bytes of the mac > + * > + * Sets the maximum frame length to be received. Frames bigger than > + * this size are aborted. > + * > + * RETURNS: > + * Returns 0 on success or a negative error code. > + */ > +static int > +lan78xx_set_rx_max_frame_length(struct muge_softc *sc, int size) > +{ > + int err = 0; > + uint32_t buf; > + bool rxenabled; > + > + /* first we have to disable rx before changing the length */ > + > + err = lan78xx_read_reg(sc, MAC_RX, &buf); > + rxenabled = ((buf & MAC_RX_EN_) != 0); > + > + if (rxenabled) { > + buf &= ~MAC_RX_EN_; > + err = lan78xx_write_reg(sc, MAC_RX, buf); > + } > + > + /* setting max frame length */ > + > + buf &= ~MAC_RX_MAX_FR_SIZE_MASK_; > + buf |= (((size + 4) << MAC_RX_MAX_FR_SIZE_SHIFT_) & > + MAC_RX_MAX_FR_SIZE_MASK_); > + err = lan78xx_write_reg(sc, MAC_RX, buf); > + > + /* If it were enabled before, we enable it back. */ > + > + if (rxenabled) { > + buf |= MAC_RX_EN_; > + err = lan78xx_write_reg(sc, MAC_RX, buf); > + } > + > + return 0; > +} > + > +/** > + * lan78xx_miibus_readreg - Read a MII/MDIO register > + * @dev: usb ether device > + * @phy: the number of phy reading from > + * @reg: the register address > + * > + * 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 > +lan78xx_miibus_readreg(device_t dev, int phy, int reg) { > + > + struct muge_softc *sc = device_get_softc(dev); > + int locked; > + uint32_t addr, val; > + > + val = 0; > + locked = mtx_owned(&sc->sc_mtx); > + if (!locked) > + MUGE_LOCK(sc); > + > + if (lan78xx_wait_for_bits(sc, MII_ACCESS, MII_BUSY_) != 0) { > + muge_warn_printf(sc, "MII is busy\n"); > + goto done; > + } > + > + addr = (phy << 11) | (reg << 6) | MII_READ_ | MII_BUSY_; > + lan78xx_write_reg(sc, MII_ACCESS, addr); > + > + if (lan78xx_wait_for_bits(sc, MII_ACCESS, MII_BUSY_) != 0) { > + muge_warn_printf(sc, "MII read timeout\n"); > + goto done; > + } > + > + lan78xx_read_reg(sc, MII_DATA, &val); > + val = le32toh(val); > + > +done: > + if (!locked) > + MUGE_UNLOCK(sc); > + > + return (val & 0xFFFF); > +} > + > +/** > + * lan78xx_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 through the usb controller > registers. > + * > + * LOCKING: > + * Takes and releases the device mutex lock if not already held. > + * > + * RETURNS: > + * Always returns 0 regardless of success or failure. > + */ > +static int > +lan78xx_miibus_writereg(device_t dev, int phy, int reg, int val) > +{ > + struct muge_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) > + MUGE_LOCK(sc); > + > + if (lan78xx_wait_for_bits(sc, MII_ACCESS, MII_BUSY_) != 0) { > + muge_warn_printf(sc, "MII is busy\n"); > + goto done; > + } > + > + val = htole32(val); > + lan78xx_write_reg(sc, MII_DATA, val); > + > + addr = (phy << 11) | (reg << 6) | MII_WRITE_ | MII_BUSY_; > + lan78xx_write_reg(sc, MII_ACCESS, addr); > + > + if (lan78xx_wait_for_bits(sc, MII_ACCESS, MII_BUSY_) != 0) > + muge_warn_printf(sc, "MII write timeout\n"); > + > +done: > + if (!locked) > + MUGE_UNLOCK(sc); > + return (0); > +} > + > +/* > + * lan78xx_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 > +lan78xx_miibus_statchg(device_t dev) > +{ > + struct muge_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 = 0; > + uint32_t fct_flow = 0; > + > + locked = mtx_owned(&sc->sc_mtx); > + if (!locked) > + MUGE_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 &= ~MUGE_FLAG_LINK; > + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == > + (IFM_ACTIVE | IFM_AVALID)) { > + muge_dbg_printf(sc, "media is active\n"); > + switch (IFM_SUBTYPE(mii->mii_media_active)) { > + case IFM_10_T: > + case IFM_100_TX: > + sc->sc_flags |= MUGE_FLAG_LINK; > + muge_dbg_printf(sc, "10/100 ethernet\n"); > + break; > + case IFM_1000_T: > + sc->sc_flags |= MUGE_FLAG_LINK; > + muge_dbg_printf(sc, "Gigabit ethernet\n"); > + break; > + default: > + break; > + } > + } > + /* Lost link, do nothing. */ > + if ((sc->sc_flags & MUGE_FLAG_LINK) == 0) { > + muge_dbg_printf(sc, "link flag not set\n"); > + goto done; > + } > + > + err = lan78xx_read_reg(sc, FCT_FLOW, &fct_flow); > + if (err) { > + muge_warn_printf(sc, > + "failed to read initial flow control thresholds, 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) { > + muge_dbg_printf(sc, "full duplex operation\n"); > + > + /* enable transmit MAC flow control function */ > + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) > + flow |= FLOW_CR_TX_FCEN_ | 0xFFFF; > + > + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) > + flow |= FLOW_CR_RX_FCEN_; > + } > + > + switch(usbd_get_speed(sc->sc_ue.ue_udev)) { > + case USB_SPEED_SUPER: > + fct_flow = 0x817; /* XXX */ > + break; > + case USB_SPEED_HIGH: > + fct_flow = 0x211; /* XXX */ > + break; > + default: > + break; > + } > + > + err += lan78xx_write_reg(sc, FLOW, flow); > + err += lan78xx_write_reg(sc, FCT_FLOW, fct_flow); > + if (err) > + muge_warn_printf(sc, "media change failed, error %d\n", err); > + > +done: > + if (!locked) > + MUGE_UNLOCK(sc); > +} > + > +/* > + * lan78xx_set_mdix_auto - Configure the device to enable automatic > + * crossover and polarity detection. LAN7800 provides HP Auto-MDIX > + * functionality for seamless crossover and polarity detection. > + * > + * @sc: driver soft context > + * > + * LOCKING: > + * Takes and releases the device mutex lock if not already held. > + */ > +static void > +lan78xx_set_mdix_auto(struct muge_softc *sc) > +{ > + uint32_t buf, err; > + > + err = lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MUGE_EXT_PAGE_ACCESS, MUGE_EXT_PAGE_SPACE_1); > + > + buf = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MUGE_EXT_MODE_CTRL); > + buf &= ~MUGE_EXT_MODE_CTRL_MDIX_MASK_; > + buf |= MUGE_EXT_MODE_CTRL_AUTO_MDIX_; > + > + lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); > + err += lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MUGE_EXT_MODE_CTRL, buf); > + > + err += lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MUGE_EXT_PAGE_ACCESS, MUGE_EXT_PAGE_SPACE_0); > + > + if (err != 0) > + muge_warn_printf(sc, "error setting PHY's MDIX status\n"); > + > + sc->sc_mdix_ctl = buf; > +} > + > +/** > + * lan78xx_phy_init - Initialises the in-built MUGE phy > + * @sc: driver soft context > + * > + * Resets the PHY part of the chip and then initialises it to default > + * values. The 'link down' and 'auto-negotiation complete' interrupts > + * from the PHY are also enabled, however we don't monitor the interrupt > + * endpoints for the moment. > + * > + * RETURNS: > + * Returns 0 on success or EIO if failed to reset the PHY. > + */ > +static int > +lan78xx_phy_init(struct muge_softc *sc) > +{ > + muge_dbg_printf(sc, "Initializing PHY.\n"); > + uint16_t bmcr; > + usb_ticks_t start_ticks; > + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); > + > + MUGE_LOCK_ASSERT(sc, MA_OWNED); > + > + /* Reset phy and wait for reset to complete */ > + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, > + BMCR_RESET); > + > + start_ticks = ticks; > + do { > + uether_pause(&sc->sc_ue, hz / 100); > + bmcr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MII_BMCR); > + } while ((bmcr & BMCR_RESET) && ((ticks - start_ticks) < max_ticks)); > + > + if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) { > + muge_err_printf(sc, "PHY reset timed-out\n"); > + return (EIO); > + } > + > + /* Setup phy to interrupt upon link down or autoneg completion. */ > + lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MUGE_PHY_INTR_STAT); > + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, > + MUGE_PHY_INTR_MASK, > + (MUGE_PHY_INTR_ANEG_COMP | MUGE_PHY_INTR_LINK_CHANGE)); > + > + /* Enable Auto-MDIX for crossover and polarity detection. */ > + lan78xx_set_mdix_auto(sc); > + > + /* Enable all modes. */ > + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR, > + ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | > + ANAR_CSMA | ANAR_FC | ANAR_PAUSE_ASYM); > + > + /* Restart auto-negotation */ > + bmcr |= BMCR_STARTNEG; > + bmcr |= BMCR_AUTOEN; > + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, > bmcr); > + bmcr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, > MII_BMCR); > + return (0); > +} > + > +/** > + * lan78xx_chip_init - Initialises the chip after power on > + * @sc: driver soft context > + * > + * This initialisation sequence is modelled on the procedure in the > Linux > + * driver. > + * > + * RETURNS: > + * Returns 0 on success or an error code on failure. > + */ > +static int > +lan78xx_chip_init(struct muge_softc *sc) > +{ > + int err; > + int locked; > + uint32_t buf; > + uint32_t burst_cap; > + > + locked = mtx_owned(&sc->sc_mtx); > + if (!locked) > + MUGE_LOCK(sc); > + > + /* Enter H/W config mode */ > + lan78xx_write_reg(sc, HW_CFG, HW_CFG_LRST_); > + > + if ((err = lan78xx_wait_for_bits(sc, HW_CFG, HW_CFG_LRST_)) != 0) { > + muge_warn_printf(sc, > + "timed-out waiting for lite reset to complete\n"); > + goto init_failed; > + } > + > + /* Set the mac address */ > + if ((err = lan78xx_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) { > + muge_warn_printf(sc, "failed to set the MAC address\n"); > + goto init_failed; > + } > + > + /* Read and display the revision register */ > + if ((err = lan78xx_read_reg(sc, ID_REV, &sc->sc_rev_id)) < 0) { > + muge_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err); > + goto init_failed; > + } > + > + device_printf(sc->sc_ue.ue_dev, "chip 0x%04lx, rev. %04lx\n", > + (sc->sc_rev_id & ID_REV_CHIP_ID_MASK_) >> 16, > + (sc->sc_rev_id & ID_REV_CHIP_REV_MASK_)); > + > + /* Respond to BULK-IN tokens with a NAK when RX FIFO is empty. */ > + if ((err = lan78xx_read_reg(sc, USB_CFG0, &buf)) != 0) { > + muge_warn_printf(sc, "failed to read USB_CFG0: %d\n", err); > + goto init_failed; > + } > + buf |= USB_CFG_BIR_; > + lan78xx_write_reg(sc, USB_CFG0, buf); > + > + /* > + * LTM support will go here. > + */ > + > + /* Configuring the burst cap. */ > + switch (usbd_get_speed(sc->sc_ue.ue_udev)) { > + case USB_SPEED_SUPER: > + burst_cap = MUGE_DEFAULT_BURST_CAP_SIZE/MUGE_SS_USB_PKT_SIZE; > > *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** > _______________________________________________ > svn-src-all@freebsd.org mailing list > https://lists.freebsd.org/mailman/listinfo/svn-src-all > To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?op.zjqsmrwikndu52>