svn commit: r333713 - in head/sys: dev/usb/net modules/usb/muge

Ronald Klop ronald-lists at klop.ws
Mon May 28 21:00:18 UTC 2018


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 at 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 at freebsd.org>.
> + * Copyright (C) 2018 The FreeBSD Foundation.
> + *
> + * This software was developed by Arshan Khanifar  
> <arshankhanifar at 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 at freebsd.org mailing list
> https://lists.freebsd.org/mailman/listinfo/svn-src-all
> To unsubscribe, send any mail to "svn-src-all-unsubscribe at freebsd.org"


More information about the svn-src-head mailing list