All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
@ 2017-04-10 19:23 Yuiko.Oshino at microchip.com
  2017-04-14 12:35 ` Marek Vasut
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Yuiko.Oshino at microchip.com @ 2017-04-10 19:23 UTC (permalink / raw)
  To: u-boot

From: Yuiko Oshino <yuiko.oshino@microchip.com>

Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers

Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
Cc: Marek Vasut <marex@denx.de>
---
 drivers/usb/Kconfig         |   2 +
 drivers/usb/eth/Kconfig     |  18 ++
 drivers/usb/eth/Makefile    |   2 +
 drivers/usb/eth/lan75xx.c   | 463 ++++++++++++++++++++++++++++++
 drivers/usb/eth/lan78xx.c   | 611 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/eth/lan7x.c     | 680 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/eth/lan7x.h     | 189 ++++++++++++
 drivers/usb/eth/usb_ether.c |  14 +
 include/usb_ether.h         |  12 +
 9 files changed, 1991 insertions(+)
 create mode 100644 drivers/usb/eth/Kconfig
 create mode 100644 drivers/usb/eth/lan75xx.c
 create mode 100644 drivers/usb/eth/lan78xx.c
 create mode 100644 drivers/usb/eth/lan7x.c
 create mode 100644 drivers/usb/eth/lan7x.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index da3ec2f..62126aa 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -94,4 +94,6 @@ endif
 
 source "drivers/usb/gadget/Kconfig"
 
+source "drivers/usb/eth/Kconfig"
+
 endif
diff --git a/drivers/usb/eth/Kconfig b/drivers/usb/eth/Kconfig
new file mode 100644
index 0000000..a07243f
--- /dev/null
+++ b/drivers/usb/eth/Kconfig
@@ -0,0 +1,18 @@
+comment "USB to Ethernet Controller Drivers"
+
+config USB_ETHER_LAN75XX
+	bool "Microchip LAN75XX support"
+	---help---
+	  Say Y here if you would like to support Microchip LAN75XX Hi-Speed
+	  USB 2.0 to 10/100/1000 Gigabit Ethernet controller.
+	  Supports 10Base-T/ 100Base-TX/1000Base-T.
+	  This driver supports the internal PHY.
+
+config USB_ETHER_LAN78XX
+	bool "Microchip LAN78XX support"
+	---help---
+	  Say Y here if you would like to support Microchip LAN78XX USB 3.1
+	  Gen 1 to 10/100/1000 Gigabit Ethernet controller.
+	  Supports 10Base-T/ 100Base-TX/1000Base-T.
+	  This driver supports the internal PHY.
+
diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile
index 4c44efc..4b935a3 100644
--- a/drivers/usb/eth/Makefile
+++ b/drivers/usb/eth/Makefile
@@ -9,4 +9,6 @@ obj-$(CONFIG_USB_ETHER_ASIX) += asix.o
 obj-$(CONFIG_USB_ETHER_ASIX88179) += asix88179.o
 obj-$(CONFIG_USB_ETHER_MCS7830) += mcs7830.o
 obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o
+obj-$(CONFIG_USB_ETHER_LAN75XX) += lan7x.o lan75xx.o
+obj-$(CONFIG_USB_ETHER_LAN78XX) += lan7x.o lan78xx.o
 obj-$(CONFIG_USB_ETHER_RTL8152) += r8152.o r8152_fw.o
diff --git a/drivers/usb/eth/lan75xx.c b/drivers/usb/eth/lan75xx.c
new file mode 100644
index 0000000..f67b216
--- /dev/null
+++ b/drivers/usb/eth/lan75xx.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <dm.h>
+#include <usb.h>
+#include <linux/mii.h>
+#include "usb_ether.h"
+#include "lan7x.h"
+
+/* SCSRs */
+#define LAN75XX_HW_CFG_BIR		BIT(7)
+
+#define LAN75XX_BURST_CAP		0x034
+
+#define LAN75XX_BULK_IN_DLY		0x03C
+
+#define LAN75XX_RFE_CTL			0x060
+
+#define LAN75XX_FCT_RX_CTL		0x090
+
+#define LAN75XX_FCT_TX_CTL		0x094
+
+#define LAN75XX_FCT_RX_FIFO_END		0x098
+
+#define LAN75XX_FCT_TX_FIFO_END		0x09C
+
+#define LAN75XX_FCT_FLOW		0x0A0
+
+/* MAC ADDRESS PERFECT FILTER For LAN75xx */
+#define LAN75XX_ADDR_FILTX		0x300
+#define LAN75XX_ADDR_FILTX_FB_VALID	BIT(31)
+
+#ifndef CONFIG_DM_ETH
+/* local vars */
+static int curr_eth_dev;	/* index for name of next device detected */
+
+/* local defines */
+#define LAN75XX_BASE_NAME  "lan75xx"
+#endif
+
+/*
+ * Lan75xx infrastructure commands
+ */
+static int lan75xx_phy_gig_workaround(struct usb_device *udev,
+				      struct ueth_data *dev)
+{
+	int ret = 0;
+
+	/* Only internal phy */
+	/* Set the phy in Gig loopback */
+	lan7x_mdio_write(udev, dev->phy_id, MII_BMCR,
+			 (BMCR_LOOPBACK | BMCR_SPEED1000));
+
+	/* Wait for the link up */
+	ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
+				      dev->phy_id, MII_BMSR, BMSR_LSTATUS,
+				      true, PHY_CONNECT_TIMEOUT_MS);
+	if (ret)
+		return ret;
+
+	/* phy reset */
+	ret = lan7x_pmt_phy_reset(udev, dev);
+	return ret;
+}
+
+static int lan75xx_update_flowcontrol(struct usb_device *udev,
+				      struct ueth_data *dev)
+{
+	uint32_t flow = 0, fct_flow = 0;
+	int ret;
+
+	ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, FLOW, flow);
+	ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow);
+	return ret;
+}
+
+static int lan75xx_read_mac(unsigned char *enetaddr,
+			    struct usb_device *udev)
+{
+	/*
+	 * Refer to the doc/README.enetaddr and doc/README.usb for
+	 * the U-Boot MAC address policy
+	 */
+	return lan7x_read_eeprom_mac(enetaddr, udev);
+}
+
+static int lan75xx_write_hwaddr_common(struct usb_device *udev,
+				       struct lan7x_private *priv,
+				       unsigned char *enetaddr)
+{
+	u32 addr_lo = get_unaligned_le32(&enetaddr[0]);
+	u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]);
+	int ret;
+
+	/* set hardware address */
+	ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX + 4, addr_lo);
+	if (ret)
+		return ret;
+
+	addr_hi |= LAN75XX_ADDR_FILTX_FB_VALID;
+	ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX, addr_hi);
+	if (ret)
+		return ret;
+
+	debug("MAC addr %pM written\n", enetaddr);
+	priv->have_hwaddr = 1;
+
+	return 0;
+}
+
+static int lan75xx_set_multicast(struct usb_device *udev)
+{
+	int ret;
+	u32 write_buf;
+
+	/* No multicast in u-boot */
+	write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
+	ret = lan7x_write_reg(udev, LAN75XX_RFE_CTL, write_buf);
+
+	return ret;
+}
+
+/* starts the TX path */
+static void lan75xx_start_tx_path(struct usb_device *udev)
+{
+	u32 reg_val;
+
+	/* Enable Tx at MAC */
+	reg_val = MAC_TX_TXEN;
+	lan7x_write_reg(udev, MAC_TX, reg_val);
+
+	/* Enable Tx at SCSRs */
+	reg_val = FCT_TX_CTL_EN;
+	lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, reg_val);
+}
+
+/* Starts the Receive path */
+static void lan75xx_start_rx_path(struct usb_device *udev)
+{
+	u32 reg_val;
+
+	/* Enable Rx at MAC */
+	reg_val = LAN7X_MAC_RX_MAX_SIZE_DEFAULT |
+		MAC_RX_FCS_STRIP | MAC_RX_RXEN;
+	lan7x_write_reg(udev, MAC_RX, reg_val);
+
+	/* Enable Rx at SCSRs */
+	reg_val = FCT_RX_CTL_EN;
+	lan7x_write_reg(udev, LAN75XX_FCT_RX_CTL, reg_val);
+}
+
+static int lan75xx_basic_reset(struct usb_device *udev,
+			       struct ueth_data *dev,
+			       struct lan7x_private *priv)
+{
+	int ret;
+	u32 write_buf;
+
+	ret = lan7x_basic_reset(udev, dev);
+	if (ret)
+		return ret;
+
+	/* Keep the chip ID */
+	ret = lan7x_read_reg(udev, ID_REV, &priv->chipid);
+	if (ret)
+		return ret;
+	debug("LAN75xx ID_REV = 0x%08x\n", priv->chipid);
+
+	priv->chipid = (priv->chipid & ID_REV_CHIP_ID_MASK) >> 16;
+
+	/* Respond to the IN token with a NAK */
+	ret = lan7x_read_reg(udev, HW_CFG, &write_buf);
+	if (ret)
+		return ret;
+	write_buf |= LAN75XX_HW_CFG_BIR;
+	ret = lan7x_write_reg(udev, HW_CFG, write_buf);
+
+	return ret;
+}
+
+static int lan75xx_init_common(struct usb_device *udev,
+			       struct ueth_data *dev,
+			       struct lan7x_private *priv,
+			       unsigned char *enetaddr)
+{
+	int ret;
+	u32 write_buf;
+
+	/* Reset and read Mac addr were done in get_info() or in probe() */
+
+#ifndef CONFIG_DM_ETH
+	if (!priv->have_hwaddr && is_valid_ethaddr(enetaddr)) {
+		priv->have_hwaddr = 1;
+		debug("LAN75xx: MAC address found and set %pM\n", enetaddr);
+	}
+#endif
+	if (!priv->have_hwaddr) {
+		printf("Error: LAN75xx: No MAC address set - set usbethaddr\n");
+		return -EADDRNOTAVAIL;
+	}
+	ret = lan75xx_write_hwaddr_common(udev, priv, enetaddr);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN75XX_BURST_CAP, 0);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN75XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY);
+	if (ret)
+		return ret;
+
+	/* set FIFO sizes */
+	write_buf = (MAX_RX_FIFO_SIZE - 512) / 512;
+	ret = lan7x_write_reg(udev, LAN75XX_FCT_RX_FIFO_END, write_buf);
+	if (ret)
+		return ret;
+
+	write_buf = (MAX_TX_FIFO_SIZE - 512) / 512;
+	ret = lan7x_write_reg(udev, LAN75XX_FCT_TX_FIFO_END, write_buf);
+	if (ret)
+		return ret;
+
+	/* Init Tx */
+	ret = lan7x_write_reg(udev, FLOW, 0);
+	if (ret)
+		return ret;
+
+	/* Init Rx. Set Vlan, keep defult for VLAN on 75xx */
+	ret = lan75xx_set_multicast(udev);
+	if (ret)
+		return ret;
+
+	/* phy workaround for gig link */
+	ret = lan75xx_phy_gig_workaround(udev, dev);
+	if (ret)
+		return ret;
+
+	/* Init PHY, autonego, and link */
+	ret = lan7x_phy_initialize(udev, dev);
+	if (ret)
+		return ret;
+
+	/*
+	 * MAC_CR has to be set after PHY init.
+	 * MAC will auto detect the PHY speed.
+	 */
+	ret = lan7x_read_reg(udev, MAC_CR, &write_buf);
+	if (ret)
+		return ret;
+	write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP;
+	ret = lan7x_write_reg(udev, MAC_CR, write_buf);
+	if (ret)
+		return ret;
+
+	lan75xx_start_tx_path(udev);
+	lan75xx_start_rx_path(udev);
+
+	ret = lan75xx_update_flowcontrol(udev, dev);
+
+	return ret;
+}
+
+#ifndef CONFIG_DM_ETH
+/*
+ * lan75xx callbacks
+ */
+static int lan75xx_init(struct eth_device *eth, bd_t *bd)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+	struct usb_device *udev = dev->pusb_dev;
+	struct lan7x_private *priv = (struct lan7x_private *)dev->dev_priv;
+
+	return lan75xx_init_common(udev, dev, priv, eth->enetaddr);
+}
+
+static int lan75xx_write_hwaddr(struct eth_device *eth)
+{
+	struct ueth_data *dev = eth->priv;
+	struct usb_device *udev = dev->pusb_dev;
+	struct lan7x_private *priv = dev->dev_priv;
+
+	return lan75xx_write_hwaddr_common(udev, priv, eth->enetaddr);
+}
+
+/*
+ * Microchip probing functions
+ */
+void lan75xx_eth_before_probe(void)
+{
+	curr_eth_dev = 0;
+}
+
+static const struct lan7x_dongle lan75xx_dongles[] = {
+	{0x0424, 0x7500},	/* LAN7500 USB Ethernet */
+	{0x0000, 0x0000}	/* END - Do not remove */
+};
+
+/* Probe to see if a new device is actually an Microchip device */
+int lan75xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+		      struct ueth_data *ss)
+{
+	int i;
+
+	/* let's examine the device now */
+	for (i = 0; lan75xx_dongles[i].vendor != 0; i++) {
+		if (dev->descriptor.idVendor == lan75xx_dongles[i].vendor &&
+		    dev->descriptor.idProduct == lan75xx_dongles[i].product)
+			/* Found a supported dongle */
+			break;
+	}
+	if (lan75xx_dongles[i].vendor == 0)
+		return 0;
+
+	/* At this point, we know we've got a live one */
+	debug("\n\nUSB Ethernet device LAN75xx detected\n");
+
+	/*
+	 * Note that this function needs to return 1
+	 * for success
+	 */
+	return lan7x_eth_probe(dev, ifnum, ss);
+}
+
+int lan75xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+			 struct eth_device *eth)
+{
+	struct lan7x_private *priv = ss->dev_priv;
+
+	printf("LAN75xx name: %s%d\n", LAN75XX_BASE_NAME, curr_eth_dev);
+
+	if (!eth) {
+		debug("%s: missing parameter.\n", __func__);
+		return 0;
+	}
+	sprintf(eth->name, "%s%d", LAN75XX_BASE_NAME, curr_eth_dev++);
+	eth->init = lan75xx_init;
+	eth->send = lan7x_send;
+	eth->recv = lan7x_recv;
+	eth->halt = lan7x_halt;
+	eth->write_hwaddr = lan75xx_write_hwaddr;
+	eth->priv = ss;
+
+	/* Do a reset in order to get the MAC address from HW */
+	if (lan75xx_basic_reset(dev, ss, priv))
+		return 0;
+
+	/* Get the MAC address */
+	/*
+	 * We must set the eth->enetaddr from HW because the upper layer
+	 * will force to use the environmental var (usbethaddr) or random if
+	 * there is no valid MAC address in eth->enetaddr.
+	 */
+	lan75xx_read_mac(eth->enetaddr, dev);
+	/* Do not return 0 for not finding MAC addr in HW */
+
+	return 1;
+}
+#endif /* !CONFIG_DM_ETH */
+
+#ifdef CONFIG_DM_ETH
+static int lan75xx_eth_start(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+
+	/* Driver-model Ethernet ensures we have this */
+	priv->have_hwaddr = 1;
+
+	return lan75xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr);
+}
+
+int lan75xx_write_hwaddr(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+
+	return lan75xx_write_hwaddr_common(udev, priv, pdata->enetaddr);
+}
+
+int lan75xx_read_rom_hwaddr(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	int ret;
+
+	ret = lan75xx_read_mac(pdata->enetaddr, udev);
+	if (ret)
+		memset(pdata->enetaddr, 0, 6);
+
+	return 0;
+}
+
+static int lan75xx_eth_probe(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+	struct ueth_data *ueth = &priv->ueth;
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+
+	/* Do a reset in order to get the MAC address from HW */
+	if (lan75xx_basic_reset(udev, ueth, priv))
+		return 0;
+
+	/* Get the MAC address */
+	/*
+	 * We must set the eth->enetaddr from HW because the upper layer
+	 * will force to use the environmental var (usbethaddr) or random if
+	 * there is no valid MAC address in eth->enetaddr.
+	 */
+	lan75xx_read_mac(pdata->enetaddr, udev);
+	/* Do not return 0 for not finding MAC addr in HW */
+
+	return usb_ether_register(dev, ueth, RX_URB_SIZE);
+}
+
+static const struct eth_ops lan75xx_eth_ops = {
+	.start	= lan75xx_eth_start,
+	.send	= lan7x_eth_send,
+	.recv	= lan7x_eth_recv,
+	.free_pkt = lan7x_free_pkt,
+	.stop	= lan7x_eth_stop,
+	.write_hwaddr = lan75xx_write_hwaddr,
+	.read_rom_hwaddr = lan75xx_read_rom_hwaddr,
+};
+
+U_BOOT_DRIVER(lan75xx_eth) = {
+	.name	= "lan75xx_eth",
+	.id	= UCLASS_ETH,
+	.probe	= lan75xx_eth_probe,
+	.ops	= &lan75xx_eth_ops,
+	.priv_auto_alloc_size = sizeof(struct lan7x_private),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+
+static const struct usb_device_id lan75xx_eth_id_table[] = {
+	{ USB_DEVICE(0x0424, 0x7500) },	/* LAN7500 USB Ethernet */
+	{ }		/* Terminating entry */
+};
+
+U_BOOT_USB_DEVICE(lan75xx_eth, lan75xx_eth_id_table);
+#endif
diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c
new file mode 100644
index 0000000..e450f2c
--- /dev/null
+++ b/drivers/usb/eth/lan78xx.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <dm.h>
+#include <usb.h>
+#include "usb_ether.h"
+#include "lan7x.h"
+
+/* LAN78xx specific register/bit defines */
+#define LAN78XX_HW_CFG_LED1_EN		BIT(21) /* Muxed with EEDO */
+#define LAN78XX_HW_CFG_LED0_EN		BIT(20) /* Muxed with EECLK */
+
+#define LAN78XX_USB_CFG0		0x080
+#define LAN78XX_USB_CFG0_BIR		BIT(6)
+
+#define LAN78XX_BURST_CAP		0x090
+
+#define LAN78XX_BULK_IN_DLY		0x094
+
+#define LAN78XX_RFE_CTL			0x0B0
+
+#define LAN78XX_FCT_RX_CTL		0x0C0
+
+#define LAN78XX_FCT_TX_CTL		0x0C4
+
+#define LAN78XX_FCT_RX_FIFO_END		0x0C8
+
+#define LAN78XX_FCT_TX_FIFO_END		0x0CC
+
+#define LAN78XX_FCT_FLOW		0x0D0
+
+#define LAN78XX_MAF_BASE		0x400
+#define LAN78XX_MAF_HIX			0x00
+#define LAN78XX_MAF_LOX			0x04
+#define LAN78XX_MAF_HI_BEGIN		(LAN78XX_MAF_BASE + LAN78XX_MAF_HIX)
+#define LAN78XX_MAF_LO_BEGIN		(LAN78XX_MAF_BASE + LAN78XX_MAF_LOX)
+#define LAN78XX_MAF_HI(index)		(LAN78XX_MAF_BASE + (8 * (index)) + \
+					(LAN78XX_MAF_HIX))
+#define LAN78XX_MAF_LO(index)		(LAN78XX_MAF_BASE + (8 * (index)) + \
+					(LAN78XX_MAF_LOX))
+#define LAN78XX_MAF_HI_VALID		BIT(31)
+
+/* OTP registers */
+#define LAN78XX_OTP_BASE_ADDR		0x00001000
+
+#define LAN78XX_OTP_PWR_DN		(LAN78XX_OTP_BASE_ADDR + 4 * 0x00)
+#define LAN78XX_OTP_PWR_DN_PWRDN_N	BIT(0)
+
+#define LAN78XX_OTP_ADDR1		(LAN78XX_OTP_BASE_ADDR + 4 * 0x01)
+#define LAN78XX_OTP_ADDR1_15_11		0x1F
+
+#define LAN78XX_OTP_ADDR2		(LAN78XX_OTP_BASE_ADDR + 4 * 0x02)
+#define LAN78XX_OTP_ADDR2_10_3		0xFF
+
+#define LAN78XX_OTP_RD_DATA		(LAN78XX_OTP_BASE_ADDR + 4 * 0x06)
+
+#define LAN78XX_OTP_FUNC_CMD		(LAN78XX_OTP_BASE_ADDR + 4 * 0x08)
+#define LAN78XX_OTP_FUNC_CMD_READ	BIT(0)
+
+#define LAN78XX_OTP_CMD_GO		(LAN78XX_OTP_BASE_ADDR + 4 * 0x0A)
+#define LAN78XX_OTP_CMD_GO_GO		BIT(0)
+
+#define LAN78XX_OTP_STATUS		(LAN78XX_OTP_BASE_ADDR + 4 * 0x0C)
+#define LAN78XX_OTP_STATUS_BUSY		BIT(0)
+
+#define LAN78XX_OTP_INDICATOR_1		0xF3
+#define LAN78XX_OTP_INDICATOR_2		0xF7
+
+#ifndef CONFIG_DM_ETH
+/* local vars */
+static int curr_eth_dev;	/* index for name of next device detected */
+
+/* local defines */
+#define LAN78XX_BASE_NAME "lan78xx"
+#endif
+
+/*
+ * Lan78xx infrastructure commands
+ */
+static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset,
+				u32 length, u8 *data)
+{
+	int i;
+	int ret;
+	u32 buf;
+
+	ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf);
+	if (ret)
+		return ret;
+
+	if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) {
+		/* clear it and wait to be cleared */
+		ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0);
+
+		ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_PWR_DN_PWRDN_N",
+					 LAN78XX_OTP_PWR_DN,
+					 LAN78XX_OTP_PWR_DN_PWRDN_N,
+					 false, 1000);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < length; i++) {
+		ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1,
+				      ((offset + i) >> 8) &
+				      LAN78XX_OTP_ADDR1_15_11);
+		ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2,
+				      ((offset + i) & LAN78XX_OTP_ADDR2_10_3));
+
+		ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD,
+				      LAN78XX_OTP_FUNC_CMD_READ);
+		ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO,
+				      LAN78XX_OTP_CMD_GO_GO);
+
+		if (ret)
+			return ret;
+
+		ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY",
+					 LAN78XX_OTP_STATUS,
+					 LAN78XX_OTP_STATUS_BUSY,
+					 false, 1000);
+		if (ret)
+			return ret;
+
+		ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf);
+
+		data[i] = (u8)(buf & 0xFF);
+	}
+
+	return 0;
+}
+
+static int lan78xx_read_otp(struct usb_device *udev, u32 offset,
+			    u32 length, u8 *data)
+{
+	u8 sig;
+	int ret;
+
+	ret = lan78xx_read_raw_otp(udev, 0, 1, &sig);
+
+	if (!ret) {
+		if (sig == LAN78XX_OTP_INDICATOR_1)
+			offset = offset;
+		else if (sig == LAN78XX_OTP_INDICATOR_2)
+			offset += 0x100;
+		else
+			return -EINVAL;
+		ret = lan78xx_read_raw_otp(udev, offset, length, data);
+	}
+	debug("LAN78x: MAC address from OTP = %pM\n", data);
+
+	return ret;
+}
+
+static int lan78xx_read_otp_mac(unsigned char *enetaddr,
+				struct usb_device *udev)
+{
+	int ret;
+
+	memset(enetaddr, 0, 6);
+
+	ret = lan78xx_read_otp(udev,
+			       EEPROM_MAC_OFFSET,
+			       ETH_ALEN,
+			       enetaddr);
+	if (!ret && is_valid_ethaddr(enetaddr)) {
+		/* eeprom values are valid so use them */
+		debug("MAC address read from OTP %pM\n", enetaddr);
+		return 0;
+	}
+	debug("MAC address read from OTP invalid %pM\n", enetaddr);
+
+	memset(enetaddr, 0, 6);
+	return -1;
+}
+
+static int lan78xx_update_flowcontrol(struct usb_device *udev,
+				      struct ueth_data *dev)
+{
+	uint32_t flow = 0, fct_flow = 0;
+	int ret;
+
+	ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, FLOW, flow);
+	ret = lan7x_write_reg(udev, LAN78XX_FCT_FLOW, fct_flow);
+	return ret;
+}
+
+static int lan78xx_read_mac(unsigned char *enetaddr,
+			    struct usb_device *udev,
+			    struct lan7x_private *priv)
+{
+	u32 val;
+	int ret;
+	int saved = 0, done = 0;
+
+	/*
+	 * Depends on chip, some EEPROM pins are muxed with LED function.
+	 * disable & restore LED function to access EEPROM.
+	 */
+	if ((priv->chipid == ID_REV_CHIP_ID_7800) ||
+	    (priv->chipid == ID_REV_CHIP_ID_7850)) {
+		ret = lan7x_read_reg(udev, HW_CFG, &val);
+		saved = val;
+		val &= ~(LAN78XX_HW_CFG_LED1_EN | LAN78XX_HW_CFG_LED0_EN);
+		ret = lan7x_write_reg(udev, HW_CFG, val);
+		if (ret)
+			goto restore;
+	}
+
+	/*
+	 * Refer to the doc/README.enetaddr and doc/README.usb for
+	 * the U-Boot MAC address policy
+	 */
+	/* try reading mac address from EEPROM, then from OTP */
+	ret = lan7x_read_eeprom_mac(enetaddr, udev);
+	if (!ret)
+		done = 1;
+
+restore:
+	if ((priv->chipid == ID_REV_CHIP_ID_7800) ||
+	    (priv->chipid == ID_REV_CHIP_ID_7850)) {
+		ret = lan7x_write_reg(udev, HW_CFG, saved);
+	}
+	/* if the EEPROM mac address is good, then exit */
+	if (done)
+		return 0;
+
+	/* try reading mac address from OTP if the device is LAN78xx */
+	ret = lan78xx_read_otp_mac(enetaddr, udev);
+
+	return ret;
+}
+
+static int lan78xx_write_hwaddr_common(struct usb_device *udev,
+				       struct lan7x_private *priv,
+				       unsigned char *enetaddr)
+{
+	u32 addr_lo = get_unaligned_le32(&enetaddr[0]);
+	u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]);
+	int ret;
+
+	/* set hardware address */
+	ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN78XX_MAF_LO(0), addr_lo);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN78XX_MAF_HI(0),
+			      addr_hi | LAN78XX_MAF_HI_VALID);
+	if (ret)
+		return ret;
+
+	debug("MAC addr %pM written\n", enetaddr);
+	priv->have_hwaddr = 1;
+
+	return 0;
+}
+
+static int lan78xx_set_multicast(struct usb_device *udev)
+{
+	int ret;
+	u32 write_buf;
+
+	/* No multicast in u-boot */
+	write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
+	ret = lan7x_write_reg(udev, LAN78XX_RFE_CTL, write_buf);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* starts the TX path */
+static void lan78xx_start_tx_path(struct usb_device *udev)
+{
+	u32 reg_val;
+
+	/* Enable Tx at MAC */
+	reg_val = MAC_TX_TXEN;
+	lan7x_write_reg(udev, MAC_TX, reg_val);
+
+	/* Enable Tx at SCSRs */
+	reg_val = FCT_TX_CTL_EN;
+	lan7x_write_reg(udev, LAN78XX_FCT_TX_CTL, reg_val);
+}
+
+/* Starts the Receive path */
+static void lan78xx_start_rx_path(struct usb_device *udev)
+{
+	u32 reg_val;
+
+	/* Enable Rx at MAC */
+	reg_val = LAN7X_MAC_RX_MAX_SIZE_DEFAULT |
+		MAC_RX_FCS_STRIP | MAC_RX_RXEN;
+	lan7x_write_reg(udev, MAC_RX, reg_val);
+
+	/* Enable Rx at SCSRs */
+	reg_val = FCT_RX_CTL_EN;
+	lan7x_write_reg(udev, LAN78XX_FCT_RX_CTL, reg_val);
+}
+
+static int lan78xx_basic_reset(struct usb_device *udev,
+			       struct ueth_data *dev,
+			       struct lan7x_private *priv)
+{
+	int ret;
+	u32 write_buf;
+
+	ret = lan7x_basic_reset(udev, dev);
+	if (ret)
+		return ret;
+
+	/* Keep the chip ID */
+	ret = lan7x_read_reg(udev, ID_REV, &priv->chipid);
+	if (ret)
+		return ret;
+	debug("LAN78xx ID_REV = 0x%08x\n", priv->chipid);
+
+	priv->chipid = (priv->chipid & ID_REV_CHIP_ID_MASK) >> 16;
+
+	/* Respond to the IN token with a NAK */
+	ret = lan7x_read_reg(udev, LAN78XX_USB_CFG0, &write_buf);
+	if (ret)
+		return ret;
+	write_buf |= LAN78XX_USB_CFG0_BIR;
+	ret = lan7x_write_reg(udev, LAN78XX_USB_CFG0, write_buf);
+
+	return ret;
+}
+
+static int lan78xx_init_common(struct usb_device *udev,
+			       struct ueth_data *dev,
+			       struct lan7x_private *priv,
+			       unsigned char *enetaddr)
+{
+	int ret;
+	u32 write_buf;
+
+	/* Reset and read Mac addr were done in get_info() or in probe() */
+
+#ifndef CONFIG_DM_ETH
+	if (!priv->have_hwaddr && is_valid_ethaddr(enetaddr)) {
+		priv->have_hwaddr = 1;
+		debug("LAN78xx: MAC address found and set %pM\n", enetaddr);
+	}
+#endif
+	if (!priv->have_hwaddr) {
+		printf("Error: LAN78xx: No MAC address set - set usbethaddr\n");
+		return -EADDRNOTAVAIL;
+	}
+	ret = lan78xx_write_hwaddr_common(udev, priv, enetaddr);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN78XX_BURST_CAP, 0);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, LAN78XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY);
+	if (ret)
+		return ret;
+
+	ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF);
+	if (ret)
+		return ret;
+
+	/* set FIFO sizes */
+	write_buf = (MAX_RX_FIFO_SIZE - 512) / 512;
+	ret = lan7x_write_reg(udev, LAN78XX_FCT_RX_FIFO_END, write_buf);
+	if (ret)
+		return ret;
+
+	write_buf = (MAX_TX_FIFO_SIZE - 512) / 512;
+	ret = lan7x_write_reg(udev, LAN78XX_FCT_TX_FIFO_END, write_buf);
+	if (ret)
+		return ret;
+
+	/* Init Tx */
+	ret = lan7x_write_reg(udev, FLOW, 0);
+	if (ret)
+		return ret;
+
+	/* Init Rx. Set Vlan, keep defult for VLAN on 78xx */
+	ret = lan78xx_set_multicast(udev);
+	if (ret < 0)
+		return ret;
+
+	/* Init PHY, autonego, and link */
+	ret = lan7x_phy_initialize(udev, dev);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * MAC_CR has to be set after PHY init.
+	 * MAC will auto detect the PHY speed.
+	 */
+	ret = lan7x_read_reg(udev, MAC_CR, &write_buf);
+	if (ret)
+		return ret;
+	write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP;
+	ret = lan7x_write_reg(udev, MAC_CR, write_buf);
+	if (ret)
+		return ret;
+
+	lan78xx_start_tx_path(udev);
+	lan78xx_start_rx_path(udev);
+
+	ret = lan78xx_update_flowcontrol(udev, dev);
+
+	return ret;
+}
+
+#ifndef CONFIG_DM_ETH
+/*
+ * lan78xx callbacks
+ */
+static int lan78xx_init(struct eth_device *eth, bd_t *bd)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+	struct usb_device *udev = dev->pusb_dev;
+	struct lan7x_private *priv = (struct lan7x_private *)dev->dev_priv;
+
+	return lan78xx_init_common(udev, dev, priv, eth->enetaddr);
+}
+
+static int lan78xx_write_hwaddr(struct eth_device *eth)
+{
+	struct ueth_data *dev = eth->priv;
+	struct usb_device *udev = dev->pusb_dev;
+	struct lan7x_private *priv = dev->dev_priv;
+
+	return lan78xx_write_hwaddr_common(udev, priv, eth->enetaddr);
+}
+
+/*
+ * Microchip probing functions
+ */
+void lan78xx_eth_before_probe(void)
+{
+	curr_eth_dev = 0;
+}
+
+static const struct lan7x_dongle lan78xx_dongles[] = {
+	{0x0424, 0x7800},	/* LAN7800 USB Ethernet */
+	{0x0424, 0x7850},	/* LAN7850 USB Ethernet */
+	{0x0000, 0x0000}	/* END - Do not remove */
+};
+
+/* Probe to see if a new device is actually an Microchip device */
+int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+		      struct ueth_data *ss)
+{
+	int i;
+
+	/* let's examine the device now */
+
+	for (i = 0; lan78xx_dongles[i].vendor != 0; i++) {
+		if (dev->descriptor.idVendor == lan78xx_dongles[i].vendor &&
+		    dev->descriptor.idProduct == lan78xx_dongles[i].product)
+			/* Found a supported dongle */
+			break;
+	}
+	if (lan78xx_dongles[i].vendor == 0)
+		return 0;
+
+	/* At this point, we know we've got a live one */
+	debug("\n\nUSB Ethernet device LAN78xx detected\n");
+
+	/*
+	 * Note that this function needs to return 1
+	 * for success
+	 */
+	return lan7x_eth_probe(dev, ifnum, ss);
+}
+
+int lan78xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+			 struct eth_device *eth)
+{
+	struct lan7x_private *priv = ss->dev_priv;
+
+	printf("LAN78xx name: %s%d\n", LAN78XX_BASE_NAME, curr_eth_dev);
+
+	if (!eth) {
+		debug("%s: missing parameter.\n", __func__);
+		return 0;
+	}
+	sprintf(eth->name, "%s%d", LAN78XX_BASE_NAME, curr_eth_dev++);
+	eth->init = lan78xx_init;
+	eth->send = lan7x_send;
+	eth->recv = lan7x_recv;
+	eth->halt = lan7x_halt;
+	eth->write_hwaddr = lan78xx_write_hwaddr;
+	eth->priv = ss;
+
+	/* Do a reset in order to get the MAC address from HW */
+	if (lan78xx_basic_reset(dev, ss, priv))
+		return 0;
+
+	/* Get the MAC address */
+	/*
+	 * We must set the eth->enetaddr from HW because the upper layer
+	 * will force to use the environmental var (usbethaddr) or random if
+	 * there is no valid MAC address in eth->enetaddr.
+	 */
+	lan78xx_read_mac(eth->enetaddr, dev, priv);
+	/* Do not return 0 for not finding MAC addr in HW */
+
+	return 1;
+}
+#endif /* !CONFIG_DM_ETH */
+
+#ifdef CONFIG_DM_ETH
+static int lan78xx_eth_start(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+
+	/* Driver-model Ethernet ensures we have this */
+	priv->have_hwaddr = 1;
+
+	return lan78xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr);
+}
+
+int lan78xx_write_hwaddr(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+
+	return lan78xx_write_hwaddr_common(udev, priv, pdata->enetaddr);
+}
+
+int lan78xx_read_rom_hwaddr(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = lan78xx_read_mac(pdata->enetaddr, udev, priv);
+	if (ret)
+		memset(pdata->enetaddr, 0, 6);
+
+	return 0;
+}
+
+static int lan78xx_eth_probe(struct udevice *dev)
+{
+	struct usb_device *udev = dev_get_parent_priv(dev);
+	struct lan7x_private *priv = dev_get_priv(dev);
+	struct ueth_data *ueth = &priv->ueth;
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+
+	/* Do a reset in order to get the MAC address from HW */
+	if (lan78xx_basic_reset(udev, ueth, priv))
+		return 0;
+
+	/* Get the MAC address */
+	/*
+	 * We must set the eth->enetaddr from HW because the upper layer
+	 * will force to use the environmental var (usbethaddr) or random if
+	 * there is no valid MAC address in eth->enetaddr.
+	 */
+	lan78xx_read_mac(pdata->enetaddr, udev, priv);
+	/* Do not return 0 for not finding MAC addr in HW */
+
+	return usb_ether_register(dev, ueth, RX_URB_SIZE);
+}
+
+static const struct eth_ops lan78xx_eth_ops = {
+	.start	= lan78xx_eth_start,
+	.send	= lan7x_eth_send,
+	.recv	= lan7x_eth_recv,
+	.free_pkt = lan7x_free_pkt,
+	.stop	= lan7x_eth_stop,
+	.write_hwaddr = lan78xx_write_hwaddr,
+	.read_rom_hwaddr = lan78xx_read_rom_hwaddr,
+};
+
+U_BOOT_DRIVER(lan78xx_eth) = {
+	.name	= "lan78xx_eth",
+	.id	= UCLASS_ETH,
+	.probe	= lan78xx_eth_probe,
+	.ops	= &lan78xx_eth_ops,
+	.priv_auto_alloc_size = sizeof(struct lan7x_private),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+
+static const struct usb_device_id lan78xx_eth_id_table[] = {
+	{ USB_DEVICE(0x0424, 0x7800) },	/* LAN7800 USB Ethernet */
+	{ USB_DEVICE(0x0424, 0x7850) },	/* LAN7850 USB Ethernet */
+	{ }		/* Terminating entry */
+};
+
+U_BOOT_USB_DEVICE(lan78xx_eth, lan78xx_eth_id_table);
+#endif
diff --git a/drivers/usb/eth/lan7x.c b/drivers/usb/eth/lan7x.c
new file mode 100644
index 0000000..baa778f
--- /dev/null
+++ b/drivers/usb/eth/lan7x.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <usb.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include "usb_ether.h"
+#include "lan7x.h"
+
+/*
+ * Lan7x infrastructure commands
+ */
+int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data)
+{
+	int len;
+	ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
+
+	cpu_to_le32s(&data);
+	tmpbuf[0] = data;
+
+	len = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			      USB_VENDOR_REQUEST_WRITE_REGISTER,
+			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, index, tmpbuf, sizeof(data),
+			      USB_CTRL_SET_TIMEOUT_MS);
+	if (len != sizeof(data)) {
+		debug("%s failed: index=%d, data=%d, len=%d",
+		      __func__, index, data, len);
+		return -EIO;
+	}
+	return 0;
+}
+
+int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data)
+{
+	int len;
+	ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
+
+	len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			      USB_VENDOR_REQUEST_READ_REGISTER,
+			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			      0, index, tmpbuf, sizeof(*data),
+			      USB_CTRL_GET_TIMEOUT_MS);
+	*data = tmpbuf[0];
+	if (len != sizeof(*data)) {
+		debug("%s failed: index=%d, len=%d", __func__, index, len);
+		return -EIO;
+	}
+
+	le32_to_cpus(data);
+	return 0;
+}
+
+/* Loop until the read is completed with timeout */
+int lan7x_wait_for_bit(struct usb_device *udev,
+		       const char *prefix, const u32 index,
+		       const u32 mask, const bool set,
+		       unsigned int timeout_ms)
+{
+	u32 val;
+
+	while (--timeout_ms) {
+		lan7x_read_reg(udev, index, &val);
+
+		if (!set)
+			val = ~val;
+
+		if ((val & mask) == mask)
+			return 0;
+
+		mdelay(1);
+	}
+
+	debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
+	      prefix, index, mask, set);
+
+	return -ETIMEDOUT;
+}
+
+static int lan7x_phy_wait_not_busy(struct usb_device *udev)
+{
+	return lan7x_wait_for_bit(udev, __func__,
+				  MII_ACC, MII_ACC_MII_BUSY,
+				  false, 100);
+}
+
+int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx)
+{
+	u32 val, addr;
+
+	/* confirm MII not busy */
+	if (lan7x_phy_wait_not_busy(udev)) {
+		debug("MII is busy in %s\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	/* set the address, index & direction (read from PHY) */
+	addr = (phy_id << 11) | (idx << 6) |
+		MII_ACC_MII_READ | MII_ACC_MII_BUSY;
+	lan7x_write_reg(udev, MII_ACC, addr);
+
+	if (lan7x_phy_wait_not_busy(udev)) {
+		debug("Timed out reading MII reg %02X\n", idx);
+		return -ETIMEDOUT;
+	}
+
+	lan7x_read_reg(udev, MII_DATA, &val);
+
+	return (u16) (val & 0xFFFF);
+}
+
+int lan7x_mdio_wait_for_bit(struct usb_device *udev,
+			    const char *prefix,
+			    int phy_id, const u32 index,
+			    const u32 mask, const bool set,
+			    unsigned int timeout_ms)
+{
+	u32 val;
+
+	while (--timeout_ms) {
+		val = lan7x_mdio_read(udev, phy_id, index);
+
+		if (!set)
+			val = ~val;
+
+		if ((val & mask) == mask)
+			return 0;
+
+		mdelay(1);
+	}
+
+	debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
+	      prefix, index, mask, set);
+
+	return -ETIMEDOUT;
+}
+
+void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, int regval)
+{
+	u32 val, addr;
+
+	/* confirm MII not busy */
+	if (lan7x_phy_wait_not_busy(udev)) {
+		debug("MII is busy in %s\n", __func__);
+		return;
+	}
+
+	val = regval;
+	lan7x_write_reg(udev, MII_DATA, val);
+
+	/* set the address, index & direction (write to PHY) */
+	addr = (phy_id << 11) | (idx << 6) |
+		MII_ACC_MII_WRITE | MII_ACC_MII_BUSY;
+	lan7x_write_reg(udev, MII_ACC, addr);
+
+	if (lan7x_phy_wait_not_busy(udev))
+		debug("Timed out writing MII reg %02X\n", idx);
+}
+
+static int lan7x_eeprom_confirm_not_busy(struct usb_device *udev)
+{
+	return lan7x_wait_for_bit(udev, __func__,
+				  E2P_CMD, E2P_CMD_EPC_BUSY,
+				  false, 100);
+}
+
+static int lan7x_wait_eeprom(struct usb_device *udev)
+{
+	return lan7x_wait_for_bit(udev, __func__,
+				  E2P_CMD,
+				  (E2P_CMD_EPC_BUSY | E2P_CMD_EPC_TIMEOUT),
+				  false, 100);
+}
+
+static int lan7x_read_eeprom(struct usb_device *udev,
+			     u32 offset, u32 length, u8 *data)
+{
+	u32 val;
+	int i, ret;
+
+	ret = lan7x_eeprom_confirm_not_busy(udev);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < length; i++) {
+		val = E2P_CMD_EPC_BUSY | E2P_CMD_EPC_CMD_READ |
+			(offset & E2P_CMD_EPC_ADDR_MASK);
+		lan7x_write_reg(udev, E2P_CMD, val);
+
+		ret = lan7x_wait_eeprom(udev);
+		if (ret)
+			return ret;
+
+		lan7x_read_reg(udev, E2P_DATA, &val);
+		data[i] = val & 0xFF;
+		offset++;
+	}
+
+	return ret;
+}
+
+/*
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ *
+ * Returns 0 on success, negative on error.
+ */
+static int mii_nway_restart(struct usb_device *udev, struct ueth_data *dev)
+{
+	int bmcr;
+	int r = -1;
+
+	/* if autoneg is off, it's an error */
+	bmcr = lan7x_mdio_read(udev, dev->phy_id, MII_BMCR);
+
+	if (bmcr & BMCR_ANENABLE) {
+		bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
+		lan7x_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr);
+		r = 0;
+	} else {
+		debug("ERROR! phy autoneg is off. BMCR = 0x%04x\n", bmcr);
+	}
+	return r;
+}
+
+int lan7x_phy_initialize(struct usb_device *udev,
+			 struct ueth_data *dev)
+{
+	int r, link_detected;
+
+	lan7x_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET);
+	r = lan7x_mdio_wait_for_bit(udev, "BMCR_RESET",
+				    dev->phy_id, MII_BMCR, BMCR_RESET,
+				    false, 1000);
+
+	lan7x_mdio_write(udev, dev->phy_id, MII_ADVERTISE,
+			 ADVERTISE_ALL | ADVERTISE_CSMA |
+			 ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+	lan7x_mdio_write(udev, dev->phy_id, MII_CTRL1000,
+			 ADVERTISE_1000FULL);
+
+	r = mii_nway_restart(udev, dev);
+	r = lan7x_mdio_wait_for_bit(udev, "BMSR_ANEGCOMPLETE",
+				    dev->phy_id, MII_BMSR, BMSR_ANEGCOMPLETE,
+				    true, PHY_CONNECT_TIMEOUT_MS);
+
+	if (r == 0) {
+		debug("phy initialised succesfully\n");
+	} else {
+		debug("phy initialised failed id=%d\n", dev->phy_id);
+		return r;
+	}
+
+	printf("LAN7x: Waiting for Ethernet connection... ");
+	r = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
+				    dev->phy_id, MII_BMSR, BMSR_LSTATUS,
+				    true, PHY_CONNECT_TIMEOUT_MS);
+
+	if (r < 0) {
+		printf("unable to connect.\n");
+		return -EIO;
+	}
+	printf("done.\n");
+
+	link_detected = lan7x_mdio_read(udev, dev->phy_id, MII_BMSR);
+	debug("MII_BMSR=0x%04x\n", link_detected);
+
+	return 0;
+}
+
+static int lan7x_mii_get_an(uint32_t advertising_reg)
+{
+	int advertising = 0;
+
+	if (advertising_reg & LPA_LPACK)
+		advertising |= ADVERTISED_Autoneg;
+	if (advertising_reg & ADVERTISE_10HALF)
+		advertising |= ADVERTISED_10baseT_Half;
+	if (advertising_reg & ADVERTISE_10FULL)
+		advertising |= ADVERTISED_10baseT_Full;
+	if (advertising_reg & ADVERTISE_100HALF)
+		advertising |= ADVERTISED_100baseT_Half;
+	if (advertising_reg & ADVERTISE_100FULL)
+		advertising |= ADVERTISED_100baseT_Full;
+
+	return advertising;
+}
+
+int lan7x_update_flowcontrol(struct usb_device *udev,
+			     struct ueth_data *dev,
+			     uint32_t *flow, uint32_t *fct_flow)
+{
+	uint32_t lcladv, rmtadv, ctrl1000, stat1000;
+	uint32_t advertising = 0, lp_advertising = 0, nego = 0;
+	uint32_t duplex = 0;
+	u8 cap = 0;
+
+	lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
+	advertising = lan7x_mii_get_an(lcladv);
+
+	rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
+	lp_advertising = lan7x_mii_get_an(rmtadv);
+
+	ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
+	stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
+
+	if (ctrl1000 & ADVERTISE_1000HALF)
+		advertising |= ADVERTISED_1000baseT_Half;
+
+	if (ctrl1000 & ADVERTISE_1000FULL)
+		advertising |= ADVERTISED_1000baseT_Full;
+
+	if (stat1000 & LPA_1000HALF)
+		lp_advertising |= ADVERTISED_1000baseT_Half;
+
+	if (stat1000 & LPA_1000FULL)
+		lp_advertising |= ADVERTISED_1000baseT_Full;
+
+	nego = advertising & lp_advertising;
+
+	debug("LAN7x linked at ");
+
+	if (nego & (ADVERTISED_1000baseT_Full | ADVERTISED_1000baseT_Half)) {
+		debug("1000 ");
+		duplex = !!(nego & ADVERTISED_1000baseT_Full);
+
+	} else if (nego & (ADVERTISED_100baseT_Full |
+		   ADVERTISED_100baseT_Half)) {
+		debug("100 ");
+		duplex = !!(nego & ADVERTISED_100baseT_Full);
+	} else {
+		debug("10 ");
+		duplex = !!(nego & ADVERTISED_10baseT_Full);
+	}
+
+	if (duplex == DUPLEX_FULL)
+		debug("full dup ");
+	else
+		debug("half dup ");
+
+	if (duplex == DUPLEX_FULL) {
+		if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
+			cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
+		} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
+			if (lcladv & ADVERTISE_PAUSE_CAP)
+				cap = FLOW_CTRL_RX;
+			else if (rmtadv & LPA_PAUSE_CAP)
+				cap = FLOW_CTRL_TX;
+		}
+		debug("TX Flow ");
+		if (cap & FLOW_CTRL_TX) {
+			*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
+			/* set fct_flow thresholds to 20% and 80% */
+			*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
+					& 0x7FUL);
+			*fct_flow <<= 8UL;
+			*fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
+					& 0x7FUL);
+			debug("EN ");
+		} else {
+			debug("DIS ");
+		}
+		debug("RX Flow ");
+		if (cap & FLOW_CTRL_RX) {
+			*flow |= FLOW_CR_RX_FCEN;
+			debug("EN");
+		} else {
+			debug("DIS");
+		}
+	}
+	debug("\n");
+	return 0;
+}
+
+int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev)
+{
+	int ret;
+
+	memset(enetaddr, 0, 6);
+
+	ret = lan7x_read_eeprom(udev, 0, 1, enetaddr);
+
+	if ((ret == 0) && (enetaddr[0] == EEPROM_INDICATOR)) {
+		ret = lan7x_read_eeprom(udev,
+					EEPROM_MAC_OFFSET, ETH_ALEN,
+					enetaddr);
+		if ((ret == 0) && is_valid_ethaddr(enetaddr)) {
+			/* eeprom values are valid so use them */
+			debug("MAC address read from EEPROM %pM\n",
+			      enetaddr);
+			return 0;
+		}
+	}
+	debug("MAC address read from EEPROM invalid %pM\n", enetaddr);
+
+	memset(enetaddr, 0, 6);
+	return -1;
+}
+
+int lan7x_pmt_phy_reset(struct usb_device *udev,
+			struct ueth_data *dev)
+{
+	int ret;
+	u32 data;
+
+	ret = lan7x_read_reg(udev, PMT_CTL, &data);
+	ret = lan7x_write_reg(udev, PMT_CTL, data | PMT_CTL_PHY_RST);
+	if (ret)
+		return ret;
+
+	/* for LAN7x, we need to check PMT_CTL_READY asserted */
+	ret = lan7x_wait_for_bit(udev, "PMT_CTL_PHY_RST",
+				 PMT_CTL, PMT_CTL_PHY_RST,
+				 false, 1000); /* could take over 125mS */
+	if (ret)
+		return ret;
+
+	ret = lan7x_wait_for_bit(udev, "PMT_CTL_READY",
+				 PMT_CTL, PMT_CTL_READY,
+				 true, 1000);
+	return ret;
+}
+
+int lan7x_basic_reset(struct usb_device *udev,
+		      struct ueth_data *dev)
+{
+	int ret;
+
+	dev->phy_id = LAN7X_INTERNAL_PHY_ID; /* fixed phy id */
+
+	ret = lan7x_write_reg(udev, HW_CFG, HW_CFG_LRST);
+	if (ret)
+		return ret;
+
+	ret = lan7x_wait_for_bit(udev, "HW_CFG_LRST",
+				 HW_CFG, HW_CFG_LRST,
+				 false, 1000);
+	if (ret)
+		return ret;
+
+	ret = lan7x_pmt_phy_reset(udev, dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int lan7x_send_common(struct ueth_data *dev, void *packet, int length)
+{
+	int err;
+	int actual_len;
+	u32 tx_cmd_a;
+	u32 tx_cmd_b;
+	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg,
+				 PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b));
+
+	debug("** %s(), len %d, buf %#x\n", __func__, length,
+	      (unsigned int)(ulong) msg);
+	if (length > PKTSIZE)
+		return -ENOSPC;
+
+	/* LAN7x disable all TX offload features for u-boot */
+	tx_cmd_a = (u32) (length & TX_CMD_A_LEN_MASK) | TX_CMD_A_FCS;
+	tx_cmd_b = 0;
+	cpu_to_le32s(&tx_cmd_a);
+	cpu_to_le32s(&tx_cmd_b);
+
+	/* prepend cmd_a and cmd_b */
+	memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
+	memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
+	memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
+	       length);
+	err = usb_bulk_msg(dev->pusb_dev,
+			   usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
+			   (void *)msg,
+			   length + sizeof(tx_cmd_a) +
+			   sizeof(tx_cmd_b),
+			   &actual_len, USB_BULK_SEND_TIMEOUT_MS);
+	debug("Tx: len = %u, actual = %u, err = %d\n",
+	      (unsigned int)(length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)),
+	      (unsigned int)actual_len, err);
+
+	return err;
+}
+
+int lan7x_recv_common(struct ueth_data *dev)
+{
+	DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, RX_URB_SIZE);
+	unsigned char *buf_ptr;
+	int err;
+	int actual_len = 0;
+	u32 packet_len = 0;
+	u32 rx_cmd_a = 0;
+
+	err = usb_bulk_msg(dev->pusb_dev,
+			   usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
+			   (void *)recv_buf, RX_URB_SIZE, &actual_len,
+			   USB_BULK_RECV_TIMEOUT_MS);
+	if (actual_len == 0) {
+		debug("Rx: actual_len = 0\n");
+		return -err;
+	}
+
+	debug("Rx: RX_URB_SIZE = %u, actual = %u, err = %d\n", RX_URB_SIZE,
+	      actual_len, err);
+	if (err != 0) {
+		debug("Rx: failed to receive\n");
+		return -err;
+	}
+	if (actual_len > RX_URB_SIZE) {
+		debug("Rx: received too many bytes %d\n", actual_len);
+		return -ENOSPC;
+	}
+
+	buf_ptr = recv_buf;
+
+	/*
+	 * No multiple Ethernet Frames per USB Packet (MEF) used
+	 * for the U-boot for now.
+	 */
+	if (actual_len > 0) {
+		if (actual_len < sizeof(rx_cmd_a)) {
+			debug("Rx: incomplete packet length\n");
+			return -EIO;
+		}
+		memcpy(&rx_cmd_a, buf_ptr, sizeof(rx_cmd_a));
+		le32_to_cpus(&rx_cmd_a);
+		if (rx_cmd_a & RX_CMD_A_RXE) {
+			debug("Rx: Error header=%#x", rx_cmd_a);
+			return -EIO;
+		}
+		packet_len = (u16) (rx_cmd_a & RX_CMD_A_LEN_MASK);
+
+		if (packet_len > actual_len - sizeof(packet_len)) {
+			debug("Rx: too large packet: %d\n", packet_len);
+			return -EIO;
+		}
+
+		/*
+		 * For LAN7x, the length in command A does not
+		 * include command A, B, and C length.
+		 * So use it as is.
+		 */
+
+		debug("Rx: cmd_a 0x%08X packet_len %d\n", rx_cmd_a, packet_len);
+
+		/* Notify net stack */
+		net_process_received_packet(buf_ptr + 10, packet_len);
+	}
+
+	return err;
+}
+
+int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
+		    struct ueth_data *ss)
+{
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *iface_desc;
+	int i;
+
+	iface = &dev->config.if_desc[ifnum];
+	iface_desc = &dev->config.if_desc[ifnum].desc;
+
+	memset(ss, '\0', sizeof(struct ueth_data));
+
+	/* Initialize the ueth_data structure with some useful info */
+	ss->ifnum = ifnum;
+	ss->pusb_dev = dev;
+	ss->subclass = iface_desc->bInterfaceSubClass;
+	ss->protocol = iface_desc->bInterfaceProtocol;
+
+	/*
+	 * We are expecting a minimum of 3 endpoints
+	 * - in, out (bulk), and int.
+	 * We will ignore any others.
+	 */
+	for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+		/* is it an BULK endpoint? */
+		if ((iface->ep_desc[i].bmAttributes &
+			USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+			if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+				ss->ep_in =
+					iface->ep_desc[i].bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+			else
+				ss->ep_out =
+					iface->ep_desc[i].bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+		}
+
+		/* is it an interrupt endpoint? */
+		if ((iface->ep_desc[i].bmAttributes &
+			USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+			ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+				USB_ENDPOINT_NUMBER_MASK;
+			ss->irqinterval = iface->ep_desc[i].bInterval;
+		}
+	}
+	debug("Endpoints In %d Out %d Int %d\n",
+	      ss->ep_in, ss->ep_out, ss->ep_int);
+
+	/* Do some basic sanity checks, and bail if we find a problem */
+	if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
+	    !ss->ep_in || !ss->ep_out || !ss->ep_int) {
+		debug("Problems with device\n");
+		return 0;
+	}
+	dev->privptr = (void *)ss;
+
+#ifndef CONFIG_DM_ETH
+	/* alloc driver private */
+	ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
+	if (!ss->dev_priv)
+		return 0;
+#endif
+
+	return 1;
+}
+
+#ifndef CONFIG_DM_ETH
+/*
+ * lan7x callbacks
+ */
+int lan7x_send(struct eth_device *eth, void *packet, int length)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+	return lan7x_send_common(dev, packet, length);
+}
+
+int lan7x_recv(struct eth_device *eth)
+{
+	struct ueth_data *dev = (struct ueth_data *)eth->priv;
+
+	return lan7x_recv_common(dev);
+}
+
+void lan7x_halt(struct eth_device *eth)
+{
+	debug("** %s()\n", __func__);
+}
+#endif /* !CONFIG_DM_ETH */
+
+#ifdef CONFIG_DM_ETH
+void lan7x_eth_stop(struct udevice *dev)
+{
+	debug("** %s()\n", __func__);
+}
+
+int lan7x_eth_send(struct udevice *dev, void *packet, int length)
+{
+	struct lan7x_private *priv = dev_get_priv(dev);
+
+	return lan7x_send_common(&priv->ueth, packet, length);
+}
+
+int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct lan7x_private *priv = dev_get_priv(dev);
+
+	return lan7x_recv_common(&priv->ueth);
+}
+
+int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len)
+{
+	struct lan7x_private *priv = dev_get_priv(dev);
+
+	packet_len = ALIGN(packet_len, 4);
+	usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
+
+	return 0;
+}
+#endif /* CONFIG_DM_ETH */
+
diff --git a/drivers/usb/eth/lan7x.h b/drivers/usb/eth/lan7x.h
new file mode 100644
index 0000000..b5c1b39
--- /dev/null
+++ b/drivers/usb/eth/lan7x.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+
+/* USB Vendor Requests */
+#define USB_VENDOR_REQUEST_WRITE_REGISTER	0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER	0xA1
+#define USB_VENDOR_REQUEST_GET_STATS		0xA2
+
+/* Tx Command A */
+#define TX_CMD_A_FCS			BIT(22)
+#define TX_CMD_A_LEN_MASK		0x000FFFFF
+
+/* Rx Command A */
+#define RX_CMD_A_RXE			BIT(18)
+#define RX_CMD_A_LEN_MASK		0x00003FFF
+
+/* SCSRs */
+#define ID_REV				0x00
+#define ID_REV_CHIP_ID_MASK		0xFFFF0000
+#define ID_REV_CHIP_ID_7500		0x7500
+#define ID_REV_CHIP_ID_7800		0x7800
+#define ID_REV_CHIP_ID_7850		0x7850
+
+#define INT_STS				0x0C
+
+#define HW_CFG				0x010
+#define HW_CFG_LRST			BIT(1)
+
+#define PMT_CTL				0x014
+#define PMT_CTL_PHY_PWRUP		BIT(10)
+#define PMT_CTL_READY			BIT(7)
+#define PMT_CTL_PHY_RST			BIT(4)
+
+#define E2P_CMD				0x040
+#define E2P_CMD_EPC_BUSY		BIT(31)
+#define E2P_CMD_EPC_CMD_READ		0x00000000
+#define E2P_CMD_EPC_TIMEOUT		BIT(10)
+#define E2P_CMD_EPC_ADDR_MASK		0x000001FF
+
+#define E2P_DATA			0x044
+
+#define RFE_CTL_BCAST_EN		BIT(10)
+#define RFE_CTL_DA_PERFECT		BIT(1)
+
+#define FCT_RX_CTL_EN			BIT(31)
+
+#define FCT_TX_CTL_EN			BIT(31)
+
+#define MAC_CR				0x100
+#define MAC_CR_ADP			BIT(13)
+#define MAC_CR_AUTO_DUPLEX		BIT(12)
+#define MAC_CR_AUTO_SPEED		BIT(11)
+
+#define MAC_RX				0x104
+#define MAC_RX_FCS_STRIP		BIT(4)
+#define MAC_RX_RXEN			BIT(0)
+
+#define MAC_TX				0x108
+#define MAC_TX_TXEN			BIT(0)
+
+#define FLOW				0x10C
+#define FLOW_CR_TX_FCEN			BIT(30)
+#define FLOW_CR_RX_FCEN			BIT(29)
+
+#define RX_ADDRH			0x118
+#define RX_ADDRL			0x11C
+
+#define MII_ACC				0x120
+#define MII_ACC_MII_READ		0x00000000
+#define MII_ACC_MII_WRITE		0x00000002
+#define MII_ACC_MII_BUSY		BIT(0)
+
+#define MII_DATA			0x124
+
+#define SS_USB_PKT_SIZE			1024
+#define HS_USB_PKT_SIZE			512
+#define FS_USB_PKT_SIZE			64
+
+#define MAX_RX_FIFO_SIZE		(12 * 1024)
+#define MAX_TX_FIFO_SIZE		(12 * 1024)
+#define DEFAULT_BULK_IN_DELAY		0x0800
+
+#define EEPROM_INDICATOR		0xA5
+#define EEPROM_MAC_OFFSET		0x01
+
+/* Some extra defines */
+#define LAN7X_INTERNAL_PHY_ID		1
+
+#define LAN7X_MAC_RX_MAX_SIZE(mtu) \
+	((mtu) << 16)			/**< Max frame size */
+#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \
+	LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */)
+
+/* Timeouts */
+#define USB_CTRL_SET_TIMEOUT_MS		5000
+#define USB_CTRL_GET_TIMEOUT_MS		5000
+#define USB_BULK_SEND_TIMEOUT_MS	5000
+#define USB_BULK_RECV_TIMEOUT_MS	5000
+#define TIMEOUT_RESOLUTION_MS		50
+#define PHY_CONNECT_TIMEOUT_MS		5000
+
+#define RX_URB_SIZE	2048
+
+/* driver private */
+struct lan7x_private {
+#ifdef CONFIG_DM_ETH
+	struct ueth_data ueth;
+#endif
+	int have_hwaddr;	/* 1 if we have a hardware MAC address */
+	u32 chipid;		/* Chip or device ID */
+};
+
+#ifndef CONFIG_DM_ETH
+struct lan7x_dongle {
+	unsigned short vendor;
+	unsigned short product;
+};
+#endif
+
+/*
+ * Lan7x infrastructure commands
+ */
+int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data);
+
+int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data);
+
+int lan7x_wait_for_bit(struct usb_device *udev,
+		       const char *prefix, const u32 index,
+		       const u32 mask, const bool set,
+		       const unsigned int timeout_ms);
+
+int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx);
+
+int lan7x_mdio_wait_for_bit(struct usb_device *udev,
+			    const char *prefix,
+			    int phy_id, const u32 index,
+			    const u32 mask, const bool set,
+			    const unsigned int timeout_ms);
+
+void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx,
+		      int regval);
+
+int lan7x_pmt_phy_reset(struct usb_device *udev,
+			struct ueth_data *dev);
+
+int lan7x_phy_initialize(struct usb_device *udev,
+			 struct ueth_data *dev);
+
+int lan7x_update_flowcontrol(struct usb_device *udev,
+			     struct ueth_data *dev,
+			     uint32_t *flow, uint32_t *fct_flow);
+
+int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev);
+
+int lan7x_basic_reset(struct usb_device *udev,
+		      struct ueth_data *dev);
+
+int lan7x_send_common(struct ueth_data *dev, void *packet, int length);
+
+int lan7x_recv_common(struct ueth_data *dev);
+
+int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
+		    struct ueth_data *ss);
+
+#ifndef CONFIG_DM_ETH
+/*
+ * lan7x callbacks
+ */
+int lan7x_send(struct eth_device *eth, void *packet, int length);
+
+int lan7x_recv(struct eth_device *eth);
+
+void lan7x_halt(struct eth_device *eth);
+#endif /* !CONFIG_DM_ETH */
+
+#ifdef CONFIG_DM_ETH
+void lan7x_eth_stop(struct udevice *dev);
+
+int lan7x_eth_send(struct udevice *dev, void *packet, int length);
+
+int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp);
+
+int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len);
+#endif /* CONFIG_DM_ETH */
+
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
index 36734e2..8f4b5e9 100644
--- a/drivers/usb/eth/usb_ether.c
+++ b/drivers/usb/eth/usb_ether.c
@@ -180,6 +180,20 @@ static const struct usb_eth_prob_dev prob_dev[] = {
 		.get_info = smsc95xx_eth_get_info,
 	},
 #endif
+#ifdef CONFIG_USB_ETHER_LAN75XX
+	{
+		.before_probe = lan75xx_eth_before_probe,
+		.probe = lan75xx_eth_probe,
+		.get_info = lan75xx_eth_get_info,
+	},
+#endif
+#ifdef CONFIG_USB_ETHER_LAN78XX
+	{
+		.before_probe = lan78xx_eth_before_probe,
+		.probe = lan78xx_eth_probe,
+		.get_info = lan78xx_eth_get_info,
+	},
+#endif
 #ifdef CONFIG_USB_ETHER_RTL8152
 	{
 		.before_probe = r8152_eth_before_probe,
diff --git a/include/usb_ether.h b/include/usb_ether.h
index 51fce4e..1990b0d 100644
--- a/include/usb_ether.h
+++ b/include/usb_ether.h
@@ -132,6 +132,18 @@ int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
 int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
 			struct eth_device *eth);
 
+void lan75xx_eth_before_probe(void);
+int lan75xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+		      struct ueth_data *ss);
+int lan75xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+			 struct eth_device *eth);
+
+void lan78xx_eth_before_probe(void);
+int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
+		      struct ueth_data *ss);
+int lan78xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
+			 struct eth_device *eth);
+
 void r8152_eth_before_probe(void);
 int r8152_eth_probe(struct usb_device *dev, unsigned int ifnum,
 		    struct ueth_data *ss);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-04-10 19:23 [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx Yuiko.Oshino at microchip.com
@ 2017-04-14 12:35 ` Marek Vasut
  2017-04-18 15:58   ` Yuiko.Oshino at microchip.com
  2017-04-18 18:18 ` Simon Glass
  2017-05-05 20:58 ` Joe Hershberger
  2 siblings, 1 reply; 11+ messages in thread
From: Marek Vasut @ 2017-04-14 12:35 UTC (permalink / raw)
  To: u-boot

On 04/10/2017 09:23 PM, Yuiko.Oshino at microchip.com wrote:
> From: Yuiko Oshino <yuiko.oshino@microchip.com>
> 
> Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
> 
> Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
> Cc: Marek Vasut <marex@denx.de>

Hi!

mostly nit-picking below, thanks for the hard work!

> +/* MAC ADDRESS PERFECT FILTER For LAN75xx */
> +#define LAN75XX_ADDR_FILTX		0x300
> +#define LAN75XX_ADDR_FILTX_FB_VALID	BIT(31)
> +
> +#ifndef CONFIG_DM_ETH

I'd just make this depend on DM and scrap the non-DM part. It's not
worth the hassle .

> +/* local vars */
> +static int curr_eth_dev;	/* index for name of next device detected */
> +
> +/* local defines */
> +#define LAN75XX_BASE_NAME  "lan75xx"
> +#endif
> +
> +/*
> + * Lan75xx infrastructure commands
> + */


[...]

> diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c
> new file mode 100644
> index 0000000..e450f2c
> --- /dev/null
> +++ b/drivers/usb/eth/lan78xx.c

[...]

> +#define LAN78XX_MAF_BASE		0x400
> +#define LAN78XX_MAF_HIX			0x00
> +#define LAN78XX_MAF_LOX			0x04
> +#define LAN78XX_MAF_HI_BEGIN		(LAN78XX_MAF_BASE + LAN78XX_MAF_HIX)
> +#define LAN78XX_MAF_LO_BEGIN		(LAN78XX_MAF_BASE + LAN78XX_MAF_LOX)
> +#define LAN78XX_MAF_HI(index)		(LAN78XX_MAF_BASE + (8 * (index)) + \
> +					(LAN78XX_MAF_HIX))
> +#define LAN78XX_MAF_LO(index)		(LAN78XX_MAF_BASE + (8 * (index)) + \
> +					(LAN78XX_MAF_LOX))

You don't need the extra parenthesis around LAN78XX_MAX_LOF (dtto for
MAF_HIX above).

> +#define LAN78XX_MAF_HI_VALID		BIT(31)


[...]

> +#ifndef CONFIG_DM_ETH

DTTO here, just dump the non-DM case :)

> +/* local vars */
> +static int curr_eth_dev;	/* index for name of next device detected */
> +
> +/* local defines */
> +#define LAN78XX_BASE_NAME "lan78xx"
> +#endif

[...]

> +static const struct lan7x_dongle lan78xx_dongles[] = {
> +	{0x0424, 0x7800},	/* LAN7800 USB Ethernet */
> +	{0x0424, 0x7850},	/* LAN7850 USB Ethernet */
> +	{0x0000, 0x0000}	/* END - Do not remove */
> +};

You can use ARRAY_SIZE() to save a few bytes maybe ?

> +/* Probe to see if a new device is actually an Microchip device */
> +int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +		      struct ueth_data *ss)
> +{
> +	int i;
> +
> +	/* let's examine the device now */
> +
> +	for (i = 0; lan78xx_dongles[i].vendor != 0; i++) {
> +		if (dev->descriptor.idVendor == lan78xx_dongles[i].vendor &&
> +		    dev->descriptor.idProduct == lan78xx_dongles[i].product)
> +			/* Found a supported dongle */
> +			break;
> +	}
> +	if (lan78xx_dongles[i].vendor == 0)
> +		return 0;
> +
> +	/* At this point, we know we've got a live one */
> +	debug("\n\nUSB Ethernet device LAN78xx detected\n");
> +
> +	/*
> +	 * Note that this function needs to return 1
> +	 * for success
> +	 */
> +	return lan7x_eth_probe(dev, ifnum, ss);
> +}

[...]

> +int lan7x_update_flowcontrol(struct usb_device *udev,
> +			     struct ueth_data *dev,
> +			     uint32_t *flow, uint32_t *fct_flow)
> +{
> +	uint32_t lcladv, rmtadv, ctrl1000, stat1000;
> +	uint32_t advertising = 0, lp_advertising = 0, nego = 0;
> +	uint32_t duplex = 0;
> +	u8 cap = 0;

Shouldn't this be split into drivers/net/phy and be part of phylib ?
This code looks kinda familiar and similar to what I saw there ...

> +	lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
> +	advertising = lan7x_mii_get_an(lcladv);
> +
> +	rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
> +	lp_advertising = lan7x_mii_get_an(rmtadv);
> +
> +	ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
> +	stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
> +
> +	if (ctrl1000 & ADVERTISE_1000HALF)
> +		advertising |= ADVERTISED_1000baseT_Half;
> +
> +	if (ctrl1000 & ADVERTISE_1000FULL)
> +		advertising |= ADVERTISED_1000baseT_Full;
> +
> +	if (stat1000 & LPA_1000HALF)
> +		lp_advertising |= ADVERTISED_1000baseT_Half;
> +
> +	if (stat1000 & LPA_1000FULL)
> +		lp_advertising |= ADVERTISED_1000baseT_Full;
> +
> +	nego = advertising & lp_advertising;
> +
> +	debug("LAN7x linked at ");
> +
> +	if (nego & (ADVERTISED_1000baseT_Full | ADVERTISED_1000baseT_Half)) {
> +		debug("1000 ");
> +		duplex = !!(nego & ADVERTISED_1000baseT_Full);
> +
> +	} else if (nego & (ADVERTISED_100baseT_Full |
> +		   ADVERTISED_100baseT_Half)) {
> +		debug("100 ");
> +		duplex = !!(nego & ADVERTISED_100baseT_Full);
> +	} else {
> +		debug("10 ");
> +		duplex = !!(nego & ADVERTISED_10baseT_Full);
> +	}
> +
> +	if (duplex == DUPLEX_FULL)
> +		debug("full dup ");
> +	else
> +		debug("half dup ");
> +
> +	if (duplex == DUPLEX_FULL) {
> +		if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
> +			cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
> +		} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
> +			if (lcladv & ADVERTISE_PAUSE_CAP)
> +				cap = FLOW_CTRL_RX;
> +			else if (rmtadv & LPA_PAUSE_CAP)
> +				cap = FLOW_CTRL_TX;
> +		}
> +		debug("TX Flow ");
> +		if (cap & FLOW_CTRL_TX) {
> +			*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
> +			/* set fct_flow thresholds to 20% and 80% */
> +			*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
> +					& 0x7FUL);

The outermost parenthesis are not needed :)

> +			*fct_flow <<= 8UL;
> +			*fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
> +					& 0x7FUL);
> +			debug("EN ");
> +		} else {
> +			debug("DIS ");
> +		}
> +		debug("RX Flow ");
> +		if (cap & FLOW_CTRL_RX) {
> +			*flow |= FLOW_CR_RX_FCEN;
> +			debug("EN");
> +		} else {
> +			debug("DIS");
> +		}
> +	}
> +	debug("\n");
> +	return 0;
> +}

[...]

> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +		    struct ueth_data *ss)
> +{
> +	struct usb_interface *iface;
> +	struct usb_interface_descriptor *iface_desc;
> +	int i;
> +
> +	iface = &dev->config.if_desc[ifnum];
> +	iface_desc = &dev->config.if_desc[ifnum].desc;
> +
> +	memset(ss, '\0', sizeof(struct ueth_data));
> +
> +	/* Initialize the ueth_data structure with some useful info */
> +	ss->ifnum = ifnum;
> +	ss->pusb_dev = dev;
> +	ss->subclass = iface_desc->bInterfaceSubClass;
> +	ss->protocol = iface_desc->bInterfaceProtocol;
> +
> +	/*
> +	 * We are expecting a minimum of 3 endpoints
> +	 * - in, out (bulk), and int.
> +	 * We will ignore any others.
> +	 */
> +	for (i = 0; i < iface_desc->bNumEndpoints; i++) {
> +		/* is it an BULK endpoint? */
> +		if ((iface->ep_desc[i].bmAttributes &
> +			USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
> +			if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
> +				ss->ep_in =
> +					iface->ep_desc[i].bEndpointAddress &
> +					USB_ENDPOINT_NUMBER_MASK;
> +			else
> +				ss->ep_out =
> +					iface->ep_desc[i].bEndpointAddress &
> +					USB_ENDPOINT_NUMBER_MASK;
> +		}
> +
> +		/* is it an interrupt endpoint? */
> +		if ((iface->ep_desc[i].bmAttributes &
> +			USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
> +			ss->ep_int = iface->ep_desc[i].bEndpointAddress &
> +				USB_ENDPOINT_NUMBER_MASK;
> +			ss->irqinterval = iface->ep_desc[i].bInterval;
> +		}
> +	}
> +	debug("Endpoints In %d Out %d Int %d\n",
> +	      ss->ep_in, ss->ep_out, ss->ep_int);
> +
> +	/* Do some basic sanity checks, and bail if we find a problem */
> +	if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
> +	    !ss->ep_in || !ss->ep_out || !ss->ep_int) {
> +		debug("Problems with device\n");
> +		return 0;
> +	}
> +	dev->privptr = (void *)ss;
> +
> +#ifndef CONFIG_DM_ETH
> +	/* alloc driver private */
> +	ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
> +	if (!ss->dev_priv)
> +		return 0;
> +#endif
> +
> +	return 1;

return 1 looks a bit weird, but maybe that's correct here .

> +}

[...]

> +/* Some extra defines */
> +#define LAN7X_INTERNAL_PHY_ID		1
> +
> +#define LAN7X_MAC_RX_MAX_SIZE(mtu) \
> +	((mtu) << 16)			/**< Max frame size */

Use just one asterisk in the comment please.

> +#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \
> +	LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */)
> +
> +/* Timeouts */
> +#define USB_CTRL_SET_TIMEOUT_MS		5000
> +#define USB_CTRL_GET_TIMEOUT_MS		5000
> +#define USB_BULK_SEND_TIMEOUT_MS	5000
> +#define USB_BULK_RECV_TIMEOUT_MS	5000
> +#define TIMEOUT_RESOLUTION_MS		50
> +#define PHY_CONNECT_TIMEOUT_MS		5000
> +
> +#define RX_URB_SIZE	2048
> +
> +/* driver private */
> +struct lan7x_private {
> +#ifdef CONFIG_DM_ETH
> +	struct ueth_data ueth;
> +#endif
> +	int have_hwaddr;	/* 1 if we have a hardware MAC address */
> +	u32 chipid;		/* Chip or device ID */
> +};

[...]

-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-04-14 12:35 ` Marek Vasut
@ 2017-04-18 15:58   ` Yuiko.Oshino at microchip.com
  2017-04-18 16:47     ` Marek Vasut
  0 siblings, 1 reply; 11+ messages in thread
From: Yuiko.Oshino at microchip.com @ 2017-04-18 15:58 UTC (permalink / raw)
  To: u-boot

From: Yuiko Oshino <yuiko.oshino@microchip.com>
> -----Original Message-----
> From: Marek Vasut [mailto:marex at denx.de]
> Sent: Friday, April 14, 2017 8:35 AM
> To: Yuiko Oshino - C18177; u-boot at lists.denx.de
> Subject: Re: [PATCH] Add support for Microchip LAN75xx and LAN78xx
> 
> On 04/10/2017 09:23 PM, Yuiko.Oshino at microchip.com wrote:
> > From: Yuiko Oshino <yuiko.oshino@microchip.com>
> >
> > Add support for Microchip LAN7500, LAN7800 and 7850, USB to
> > 10/100/1000 Ethernet Controllers
> >
> > Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
> > Cc: Marek Vasut <marex@denx.de>
> 
> Hi!
> 
> mostly nit-picking below, thanks for the hard work!

Marek!
Thank you for your hard work too!

> 
> > +/* MAC ADDRESS PERFECT FILTER For LAN75xx */
> > +#define LAN75XX_ADDR_FILTX		0x300
> > +#define LAN75XX_ADDR_FILTX_FB_VALID	BIT(31)
> > +
> > +#ifndef CONFIG_DM_ETH
> 
> I'd just make this depend on DM and scrap the non-DM part. It's not worth
> the hassle .

Okay, please let me confirm that I can delete all #ifndef CONFIG_DM_ETH stuff and remove non-DM code?
Do I still need to keep #ifdef CONFIG_DM_ETH to avoid build errors?

> 
> > +/* local vars */
> > +static int curr_eth_dev;	/* index for name of next device detected */
> > +
> > +/* local defines */
> > +#define LAN75XX_BASE_NAME  "lan75xx"
> > +#endif
> > +
> > +/*
> > + * Lan75xx infrastructure commands
> > + */
> 
> 
> [...]
> 
> > diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c new
> > file mode 100644 index 0000000..e450f2c
> > --- /dev/null
> > +++ b/drivers/usb/eth/lan78xx.c
> 
> [...]
> 
> > +#define LAN78XX_MAF_BASE		0x400
> > +#define LAN78XX_MAF_HIX			0x00
> > +#define LAN78XX_MAF_LOX			0x04
> > +#define LAN78XX_MAF_HI_BEGIN		(LAN78XX_MAF_BASE +
> LAN78XX_MAF_HIX)
> > +#define LAN78XX_MAF_LO_BEGIN		(LAN78XX_MAF_BASE +
> LAN78XX_MAF_LOX)
> > +#define LAN78XX_MAF_HI(index)		(LAN78XX_MAF_BASE + (8 *
> (index)) + \
> > +					(LAN78XX_MAF_HIX))
> > +#define LAN78XX_MAF_LO(index)		(LAN78XX_MAF_BASE + (8 *
> (index)) + \
> > +					(LAN78XX_MAF_LOX))
> 
> You don't need the extra parenthesis around LAN78XX_MAX_LOF (dtto for
> MAF_HIX above).

Thank you. Will remove the extras.

> 
> > +#define LAN78XX_MAF_HI_VALID		BIT(31)
> 
> 
> [...]
> 
> > +#ifndef CONFIG_DM_ETH
> 
> DTTO here, just dump the non-DM case :)
> 
> > +/* local vars */
> > +static int curr_eth_dev;	/* index for name of next device detected */
> > +
> > +/* local defines */
> > +#define LAN78XX_BASE_NAME "lan78xx"
> > +#endif
> 
> [...]
> 
> > +static const struct lan7x_dongle lan78xx_dongles[] = {
> > +	{0x0424, 0x7800},	/* LAN7800 USB Ethernet */
> > +	{0x0424, 0x7850},	/* LAN7850 USB Ethernet */
> > +	{0x0000, 0x0000}	/* END - Do not remove */
> > +};
> 
> You can use ARRAY_SIZE() to save a few bytes maybe ?

Okay, I just followed what all other USB to Ether drivers do.
But I can remove the "Do not remove" entry to save you a few and use the ARRAY_SIZE.

> 
> > +/* Probe to see if a new device is actually an Microchip device */
> > +int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
> > +		      struct ueth_data *ss)
> > +{
> > +	int i;
> > +
> > +	/* let's examine the device now */
> > +
> > +	for (i = 0; lan78xx_dongles[i].vendor != 0; i++) {
> > +		if (dev->descriptor.idVendor == lan78xx_dongles[i].vendor
> &&
> > +		    dev->descriptor.idProduct == lan78xx_dongles[i].product)
> > +			/* Found a supported dongle */
> > +			break;
> > +	}
> > +	if (lan78xx_dongles[i].vendor == 0)
> > +		return 0;
> > +
> > +	/* At this point, we know we've got a live one */
> > +	debug("\n\nUSB Ethernet device LAN78xx detected\n");
> > +
> > +	/*
> > +	 * Note that this function needs to return 1
> > +	 * for success
> > +	 */
> > +	return lan7x_eth_probe(dev, ifnum, ss);
> > +}
> 
> [...]
> 
> > +int lan7x_update_flowcontrol(struct usb_device *udev,
> > +			     struct ueth_data *dev,
> > +			     uint32_t *flow, uint32_t *fct_flow)
> > +{
> > +	uint32_t lcladv, rmtadv, ctrl1000, stat1000;
> > +	uint32_t advertising = 0, lp_advertising = 0, nego = 0;
> > +	uint32_t duplex = 0;
> > +	u8 cap = 0;
> 
> Shouldn't this be split into drivers/net/phy and be part of phylib ?
> This code looks kinda familiar and similar to what I saw there ...

All I need is to get the auto negotiated duplex mode so that I can set the flow control.
I am going to use our device specific status registers instead of the standard registers to get the duplex status to simplify the situation.

> 
> > +	lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
> > +	advertising = lan7x_mii_get_an(lcladv);
> > +
> > +	rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
> > +	lp_advertising = lan7x_mii_get_an(rmtadv);
> > +
> > +	ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
> > +	stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
> > +
> > +	if (ctrl1000 & ADVERTISE_1000HALF)
> > +		advertising |= ADVERTISED_1000baseT_Half;
> > +
> > +	if (ctrl1000 & ADVERTISE_1000FULL)
> > +		advertising |= ADVERTISED_1000baseT_Full;
> > +
> > +	if (stat1000 & LPA_1000HALF)
> > +		lp_advertising |= ADVERTISED_1000baseT_Half;
> > +
> > +	if (stat1000 & LPA_1000FULL)
> > +		lp_advertising |= ADVERTISED_1000baseT_Full;
> > +
> > +	nego = advertising & lp_advertising;
> > +
> > +	debug("LAN7x linked at ");
> > +
> > +	if (nego & (ADVERTISED_1000baseT_Full |
> ADVERTISED_1000baseT_Half)) {
> > +		debug("1000 ");
> > +		duplex = !!(nego & ADVERTISED_1000baseT_Full);
> > +
> > +	} else if (nego & (ADVERTISED_100baseT_Full |
> > +		   ADVERTISED_100baseT_Half)) {
> > +		debug("100 ");
> > +		duplex = !!(nego & ADVERTISED_100baseT_Full);
> > +	} else {
> > +		debug("10 ");
> > +		duplex = !!(nego & ADVERTISED_10baseT_Full);
> > +	}
> > +
> > +	if (duplex == DUPLEX_FULL)
> > +		debug("full dup ");
> > +	else
> > +		debug("half dup ");
> > +
> > +	if (duplex == DUPLEX_FULL) {
> > +		if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
> > +			cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
> > +		} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
> > +			if (lcladv & ADVERTISE_PAUSE_CAP)
> > +				cap = FLOW_CTRL_RX;
> > +			else if (rmtadv & LPA_PAUSE_CAP)
> > +				cap = FLOW_CTRL_TX;
> > +		}
> > +		debug("TX Flow ");
> > +		if (cap & FLOW_CTRL_TX) {
> > +			*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
> > +			/* set fct_flow thresholds to 20% and 80% */
> > +			*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
> > +					& 0x7FUL);
> 
> The outermost parenthesis are not needed :)

Thank you again!

> 
> > +			*fct_flow <<= 8UL;
> > +			*fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
> > +					& 0x7FUL);
> > +			debug("EN ");
> > +		} else {
> > +			debug("DIS ");
> > +		}
> > +		debug("RX Flow ");
> > +		if (cap & FLOW_CTRL_RX) {
> > +			*flow |= FLOW_CR_RX_FCEN;
> > +			debug("EN");
> > +		} else {
> > +			debug("DIS");
> > +		}
> > +	}
> > +	debug("\n");
> > +	return 0;
> > +}
> 
> [...]
> 
> > +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
> > +		    struct ueth_data *ss)
> > +{
> > +	struct usb_interface *iface;
> > +	struct usb_interface_descriptor *iface_desc;
> > +	int i;
> > +
> > +	iface = &dev->config.if_desc[ifnum];
> > +	iface_desc = &dev->config.if_desc[ifnum].desc;
> > +
> > +	memset(ss, '\0', sizeof(struct ueth_data));
> > +
> > +	/* Initialize the ueth_data structure with some useful info */
> > +	ss->ifnum = ifnum;
> > +	ss->pusb_dev = dev;
> > +	ss->subclass = iface_desc->bInterfaceSubClass;
> > +	ss->protocol = iface_desc->bInterfaceProtocol;
> > +
> > +	/*
> > +	 * We are expecting a minimum of 3 endpoints
> > +	 * - in, out (bulk), and int.
> > +	 * We will ignore any others.
> > +	 */
> > +	for (i = 0; i < iface_desc->bNumEndpoints; i++) {
> > +		/* is it an BULK endpoint? */
> > +		if ((iface->ep_desc[i].bmAttributes &
> > +			USB_ENDPOINT_XFERTYPE_MASK) ==
> USB_ENDPOINT_XFER_BULK) {
> > +			if (iface->ep_desc[i].bEndpointAddress &
> USB_DIR_IN)
> > +				ss->ep_in =
> > +					iface->ep_desc[i].bEndpointAddress
> &
> > +					USB_ENDPOINT_NUMBER_MASK;
> > +			else
> > +				ss->ep_out =
> > +					iface->ep_desc[i].bEndpointAddress
> &
> > +					USB_ENDPOINT_NUMBER_MASK;
> > +		}
> > +
> > +		/* is it an interrupt endpoint? */
> > +		if ((iface->ep_desc[i].bmAttributes &
> > +			USB_ENDPOINT_XFERTYPE_MASK) ==
> USB_ENDPOINT_XFER_INT) {
> > +			ss->ep_int = iface->ep_desc[i].bEndpointAddress &
> > +				USB_ENDPOINT_NUMBER_MASK;
> > +			ss->irqinterval = iface->ep_desc[i].bInterval;
> > +		}
> > +	}
> > +	debug("Endpoints In %d Out %d Int %d\n",
> > +	      ss->ep_in, ss->ep_out, ss->ep_int);
> > +
> > +	/* Do some basic sanity checks, and bail if we find a problem */
> > +	if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
> > +	    !ss->ep_in || !ss->ep_out || !ss->ep_int) {
> > +		debug("Problems with device\n");
> > +		return 0;
> > +	}
> > +	dev->privptr = (void *)ss;
> > +
> > +#ifndef CONFIG_DM_ETH
> > +	/* alloc driver private */
> > +	ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
> > +	if (!ss->dev_priv)
> > +		return 0;
> > +#endif
> > +
> > +	return 1;
> 
> return 1 looks a bit weird, but maybe that's correct here .

Yes, the caller needs to see 1 for a successful probe.

> 
> > +}
> 
> [...]
> 
> > +/* Some extra defines */
> > +#define LAN7X_INTERNAL_PHY_ID		1
> > +
> > +#define LAN7X_MAC_RX_MAX_SIZE(mtu) \
> > +	((mtu) << 16)			/**< Max frame size */
> 
> Use just one asterisk in the comment please.

Thank you.

> 
> > +#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \
> > +	LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /*
> CRC */)
> > +
> > +/* Timeouts */
> > +#define USB_CTRL_SET_TIMEOUT_MS		5000
> > +#define USB_CTRL_GET_TIMEOUT_MS		5000
> > +#define USB_BULK_SEND_TIMEOUT_MS	5000
> > +#define USB_BULK_RECV_TIMEOUT_MS	5000
> > +#define TIMEOUT_RESOLUTION_MS		50
> > +#define PHY_CONNECT_TIMEOUT_MS		5000
> > +
> > +#define RX_URB_SIZE	2048
> > +
> > +/* driver private */
> > +struct lan7x_private {
> > +#ifdef CONFIG_DM_ETH
> > +	struct ueth_data ueth;
> > +#endif
> > +	int have_hwaddr;	/* 1 if we have a hardware MAC address */
> > +	u32 chipid;		/* Chip or device ID */
> > +};
> 
> [...]
> 
> --
> Best regards,
> Marek Vasut

Best regards,
Yuiko

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-04-18 15:58   ` Yuiko.Oshino at microchip.com
@ 2017-04-18 16:47     ` Marek Vasut
  2017-05-03 14:20       ` Yuiko.Oshino at microchip.com
  0 siblings, 1 reply; 11+ messages in thread
From: Marek Vasut @ 2017-04-18 16:47 UTC (permalink / raw)
  To: u-boot

On 04/18/2017 05:58 PM, Yuiko.Oshino at microchip.com wrote:

[...]

>>> +/* MAC ADDRESS PERFECT FILTER For LAN75xx */
>>> +#define LAN75XX_ADDR_FILTX		0x300
>>> +#define LAN75XX_ADDR_FILTX_FB_VALID	BIT(31)
>>> +
>>> +#ifndef CONFIG_DM_ETH
>>
>> I'd just make this depend on DM and scrap the non-DM part. It's not worth
>> the hassle .
> 
> Okay, please let me confirm that I can delete all #ifndef CONFIG_DM_ETH stuff and remove non-DM code?
> Do I still need to keep #ifdef CONFIG_DM_ETH to avoid build errors?

Make it depend on DM_ETH and be done with it. We're moving toward DM anyway.

I'm CCing Simon , so wait for his confirmation on this.

>>> +/* local vars */
>>> +static int curr_eth_dev;	/* index for name of next device detected */
>>> +
>>> +/* local defines */
>>> +#define LAN75XX_BASE_NAME  "lan75xx"
>>> +#endif
>>> +
>>> +/*
>>> + * Lan75xx infrastructure commands
>>> + */

[...]

>>> +int lan7x_update_flowcontrol(struct usb_device *udev,
>>> +			     struct ueth_data *dev,
>>> +			     uint32_t *flow, uint32_t *fct_flow)
>>> +{
>>> +	uint32_t lcladv, rmtadv, ctrl1000, stat1000;
>>> +	uint32_t advertising = 0, lp_advertising = 0, nego = 0;
>>> +	uint32_t duplex = 0;
>>> +	u8 cap = 0;
>>
>> Shouldn't this be split into drivers/net/phy and be part of phylib ?
>> This code looks kinda familiar and similar to what I saw there ...
> 
> All I need is to get the auto negotiated duplex mode so that I can set the flow control.
> I am going to use our device specific status registers instead of the standard registers to get the duplex status to simplify the situation.

OK, CCing Joe, I clearly don't know enough to judge if this is good or
not :)

>>> +	lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
>>> +	advertising = lan7x_mii_get_an(lcladv);
>>> +
>>> +	rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
>>> +	lp_advertising = lan7x_mii_get_an(rmtadv);
>>> +
>>> +	ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
>>> +	stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
>>> +
>>> +	if (ctrl1000 & ADVERTISE_1000HALF)
>>> +		advertising |= ADVERTISED_1000baseT_Half;
>>> +
>>> +	if (ctrl1000 & ADVERTISE_1000FULL)
>>> +		advertising |= ADVERTISED_1000baseT_Full;
>>> +
>>> +	if (stat1000 & LPA_1000HALF)
>>> +		lp_advertising |= ADVERTISED_1000baseT_Half;
>>> +
>>> +	if (stat1000 & LPA_1000FULL)
>>> +		lp_advertising |= ADVERTISED_1000baseT_Full;
>>> +
>>> +	nego = advertising & lp_advertising;
>>> +
>>> +	debug("LAN7x linked at ");
>>> +
>>> +	if (nego & (ADVERTISED_1000baseT_Full |
>> ADVERTISED_1000baseT_Half)) {
>>> +		debug("1000 ");
>>> +		duplex = !!(nego & ADVERTISED_1000baseT_Full);
>>> +
>>> +	} else if (nego & (ADVERTISED_100baseT_Full |
>>> +		   ADVERTISED_100baseT_Half)) {
>>> +		debug("100 ");
>>> +		duplex = !!(nego & ADVERTISED_100baseT_Full);
>>> +	} else {
>>> +		debug("10 ");
>>> +		duplex = !!(nego & ADVERTISED_10baseT_Full);
>>> +	}
>>> +
>>> +	if (duplex == DUPLEX_FULL)
>>> +		debug("full dup ");
>>> +	else
>>> +		debug("half dup ");
>>> +
>>> +	if (duplex == DUPLEX_FULL) {
>>> +		if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
>>> +			cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
>>> +		} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
>>> +			if (lcladv & ADVERTISE_PAUSE_CAP)
>>> +				cap = FLOW_CTRL_RX;
>>> +			else if (rmtadv & LPA_PAUSE_CAP)
>>> +				cap = FLOW_CTRL_TX;
>>> +		}
>>> +		debug("TX Flow ");
>>> +		if (cap & FLOW_CTRL_TX) {
>>> +			*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
>>> +			/* set fct_flow thresholds to 20% and 80% */
>>> +			*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
>>> +					& 0x7FUL);
>>

The rest is fine, thanks!

-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-04-10 19:23 [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx Yuiko.Oshino at microchip.com
  2017-04-14 12:35 ` Marek Vasut
@ 2017-04-18 18:18 ` Simon Glass
  2017-05-05 20:58 ` Joe Hershberger
  2 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2017-04-18 18:18 UTC (permalink / raw)
  To: u-boot

On 10 April 2017 at 13:23,  <Yuiko.Oshino@microchip.com> wrote:
> From: Yuiko Oshino <yuiko.oshino@microchip.com>
>
> Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
>
> Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
> Cc: Marek Vasut <marex@denx.de>
> ---
>  drivers/usb/Kconfig         |   2 +
>  drivers/usb/eth/Kconfig     |  18 ++
>  drivers/usb/eth/Makefile    |   2 +
>  drivers/usb/eth/lan75xx.c   | 463 ++++++++++++++++++++++++++++++
>  drivers/usb/eth/lan78xx.c   | 611 +++++++++++++++++++++++++++++++++++++++
>  drivers/usb/eth/lan7x.c     | 680 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/eth/lan7x.h     | 189 ++++++++++++
>  drivers/usb/eth/usb_ether.c |  14 +
>  include/usb_ether.h         |  12 +
>  9 files changed, 1991 insertions(+)
>  create mode 100644 drivers/usb/eth/Kconfig
>  create mode 100644 drivers/usb/eth/lan75xx.c
>  create mode 100644 drivers/usb/eth/lan78xx.c
>  create mode 100644 drivers/usb/eth/lan7x.c
>  create mode 100644 drivers/usb/eth/lan7x.h
>

Please can you remove the non-DM code? New features should use driver
model and there is no need to support the old way on new drivers. It
encourages people to migrate.

Regards,
Simon

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-04-18 16:47     ` Marek Vasut
@ 2017-05-03 14:20       ` Yuiko.Oshino at microchip.com
  0 siblings, 0 replies; 11+ messages in thread
From: Yuiko.Oshino at microchip.com @ 2017-05-03 14:20 UTC (permalink / raw)
  To: u-boot

>-----Original Message-----
>From: Marek Vasut [mailto:marex at denx.de]
>Sent: Tuesday, April 18, 2017 12:47 PM
>To: u-boot at lists.denx.de
>Cc: Yuiko Oshino - C18177; Simon Glass; Joe Hershberger
>Subject: Re: [PATCH] Add support for Microchip LAN75xx and LAN78xx
>
>On 04/18/2017 05:58 PM, Yuiko.Oshino at microchip.com wrote:
>
>[...]
>
>>>> +int lan7x_update_flowcontrol(struct usb_device *udev,
>>>> +			     struct ueth_data *dev,
>>>> +			     uint32_t *flow, uint32_t *fct_flow) {
>>>> +	uint32_t lcladv, rmtadv, ctrl1000, stat1000;
>>>> +	uint32_t advertising = 0, lp_advertising = 0, nego = 0;
>>>> +	uint32_t duplex = 0;
>>>> +	u8 cap = 0;
>>>
>>> Shouldn't this be split into drivers/net/phy and be part of phylib ?
>>> This code looks kinda familiar and similar to what I saw there ...
>>
>> All I need is to get the auto negotiated duplex mode so that I can set the flow
>control.
>> I am going to use our device specific status registers instead of the standard
>registers to get the duplex status to simplify the situation.
>
>OK, CCing Joe, I clearly don't know enough to judge if this is good or not :)
>

Joe, could you please review this and reply back?
I can read only one of our vendor specific registers to get the duplex used. (Although the register offset is not compatible between those two device families).
Please let me know what you think.
Thank you in advance.
Yuiko 

>>>> +	lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
>>>> +	advertising = lan7x_mii_get_an(lcladv);
>>>> +
>>>> +	rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
>>>> +	lp_advertising = lan7x_mii_get_an(rmtadv);
>>>> +
>>>> +	ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
>>>> +	stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
>>>> +
>>>> +	if (ctrl1000 & ADVERTISE_1000HALF)
>>>> +		advertising |= ADVERTISED_1000baseT_Half;
>>>> +
>>>> +	if (ctrl1000 & ADVERTISE_1000FULL)
>>>> +		advertising |= ADVERTISED_1000baseT_Full;
>>>> +
>>>> +	if (stat1000 & LPA_1000HALF)
>>>> +		lp_advertising |= ADVERTISED_1000baseT_Half;
>>>> +
>>>> +	if (stat1000 & LPA_1000FULL)
>>>> +		lp_advertising |= ADVERTISED_1000baseT_Full;
>>>> +
>>>> +	nego = advertising & lp_advertising;
>>>> +
>>>> +	debug("LAN7x linked at ");
>>>> +
>>>> +	if (nego & (ADVERTISED_1000baseT_Full |
>>> ADVERTISED_1000baseT_Half)) {
>>>> +		debug("1000 ");
>>>> +		duplex = !!(nego & ADVERTISED_1000baseT_Full);
>>>> +
>>>> +	} else if (nego & (ADVERTISED_100baseT_Full |
>>>> +		   ADVERTISED_100baseT_Half)) {
>>>> +		debug("100 ");
>>>> +		duplex = !!(nego & ADVERTISED_100baseT_Full);
>>>> +	} else {
>>>> +		debug("10 ");
>>>> +		duplex = !!(nego & ADVERTISED_10baseT_Full);
>>>> +	}
>>>> +
>>>> +	if (duplex == DUPLEX_FULL)
>>>> +		debug("full dup ");
>>>> +	else
>>>> +		debug("half dup ");
>>>> +
>>>> +	if (duplex == DUPLEX_FULL) {
>>>> +		if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
>>>> +			cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
>>>> +		} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
>>>> +			if (lcladv & ADVERTISE_PAUSE_CAP)
>>>> +				cap = FLOW_CTRL_RX;
>>>> +			else if (rmtadv & LPA_PAUSE_CAP)
>>>> +				cap = FLOW_CTRL_TX;
>>>> +		}
>>>> +		debug("TX Flow ");
>>>> +		if (cap & FLOW_CTRL_TX) {
>>>> +			*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
>>>> +			/* set fct_flow thresholds to 20% and 80% */
>>>> +			*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
>>>> +					& 0x7FUL);
>>>
>
>The rest is fine, thanks!
>
>--
>Best regards,
>Marek Vasut

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-04-10 19:23 [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx Yuiko.Oshino at microchip.com
  2017-04-14 12:35 ` Marek Vasut
  2017-04-18 18:18 ` Simon Glass
@ 2017-05-05 20:58 ` Joe Hershberger
  2017-05-10 15:25   ` Yuiko.Oshino at microchip.com
  2 siblings, 1 reply; 11+ messages in thread
From: Joe Hershberger @ 2017-05-05 20:58 UTC (permalink / raw)
  To: u-boot

Hi Yuiko,

On Mon, Apr 10, 2017 at 2:23 PM,  <Yuiko.Oshino@microchip.com> wrote:
> From: Yuiko Oshino <yuiko.oshino@microchip.com>
>
> Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
>
> Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
> Cc: Marek Vasut <marex@denx.de>
> ---
>  drivers/usb/Kconfig         |   2 +
>  drivers/usb/eth/Kconfig     |  18 ++
>  drivers/usb/eth/Makefile    |   2 +
>  drivers/usb/eth/lan75xx.c   | 463 ++++++++++++++++++++++++++++++
>  drivers/usb/eth/lan78xx.c   | 611 +++++++++++++++++++++++++++++++++++++++
>  drivers/usb/eth/lan7x.c     | 680 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/eth/lan7x.h     | 189 ++++++++++++
>  drivers/usb/eth/usb_ether.c |  14 +
>  include/usb_ether.h         |  12 +
>  9 files changed, 1991 insertions(+)
>  create mode 100644 drivers/usb/eth/Kconfig
>  create mode 100644 drivers/usb/eth/lan75xx.c
>  create mode 100644 drivers/usb/eth/lan78xx.c
>  create mode 100644 drivers/usb/eth/lan7x.c
>  create mode 100644 drivers/usb/eth/lan7x.h
>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index da3ec2f..62126aa 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -94,4 +94,6 @@ endif
>
>  source "drivers/usb/gadget/Kconfig"
>
> +source "drivers/usb/eth/Kconfig"
> +
>  endif
> diff --git a/drivers/usb/eth/Kconfig b/drivers/usb/eth/Kconfig
> new file mode 100644
> index 0000000..a07243f
> --- /dev/null
> +++ b/drivers/usb/eth/Kconfig
> @@ -0,0 +1,18 @@
> +comment "USB to Ethernet Controller Drivers"
> +
> +config USB_ETHER_LAN75XX
> +       bool "Microchip LAN75XX support"
> +       ---help---
> +         Say Y here if you would like to support Microchip LAN75XX Hi-Speed
> +         USB 2.0 to 10/100/1000 Gigabit Ethernet controller.
> +         Supports 10Base-T/ 100Base-TX/1000Base-T.
> +         This driver supports the internal PHY.
> +
> +config USB_ETHER_LAN78XX
> +       bool "Microchip LAN78XX support"
> +       ---help---
> +         Say Y here if you would like to support Microchip LAN78XX USB 3.1
> +         Gen 1 to 10/100/1000 Gigabit Ethernet controller.
> +         Supports 10Base-T/ 100Base-TX/1000Base-T.
> +         This driver supports the internal PHY.
> +
> diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile
> index 4c44efc..4b935a3 100644
> --- a/drivers/usb/eth/Makefile
> +++ b/drivers/usb/eth/Makefile
> @@ -9,4 +9,6 @@ obj-$(CONFIG_USB_ETHER_ASIX) += asix.o
>  obj-$(CONFIG_USB_ETHER_ASIX88179) += asix88179.o
>  obj-$(CONFIG_USB_ETHER_MCS7830) += mcs7830.o
>  obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o
> +obj-$(CONFIG_USB_ETHER_LAN75XX) += lan7x.o lan75xx.o
> +obj-$(CONFIG_USB_ETHER_LAN78XX) += lan7x.o lan78xx.o
>  obj-$(CONFIG_USB_ETHER_RTL8152) += r8152.o r8152_fw.o
> diff --git a/drivers/usb/eth/lan75xx.c b/drivers/usb/eth/lan75xx.c
> new file mode 100644
> index 0000000..f67b216
> --- /dev/null
> +++ b/drivers/usb/eth/lan75xx.c
> @@ -0,0 +1,463 @@
> +/*
> + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <dm.h>
> +#include <usb.h>
> +#include <linux/mii.h>
> +#include "usb_ether.h"
> +#include "lan7x.h"
> +
> +/* SCSRs */
> +#define LAN75XX_HW_CFG_BIR             BIT(7)
> +
> +#define LAN75XX_BURST_CAP              0x034
> +
> +#define LAN75XX_BULK_IN_DLY            0x03C
> +
> +#define LAN75XX_RFE_CTL                        0x060
> +
> +#define LAN75XX_FCT_RX_CTL             0x090
> +
> +#define LAN75XX_FCT_TX_CTL             0x094
> +
> +#define LAN75XX_FCT_RX_FIFO_END                0x098
> +
> +#define LAN75XX_FCT_TX_FIFO_END                0x09C
> +
> +#define LAN75XX_FCT_FLOW               0x0A0
> +
> +/* MAC ADDRESS PERFECT FILTER For LAN75xx */
> +#define LAN75XX_ADDR_FILTX             0x300
> +#define LAN75XX_ADDR_FILTX_FB_VALID    BIT(31)
> +
> +#ifndef CONFIG_DM_ETH
> +/* local vars */
> +static int curr_eth_dev;       /* index for name of next device detected */
> +
> +/* local defines */
> +#define LAN75XX_BASE_NAME  "lan75xx"
> +#endif
> +
> +/*
> + * Lan75xx infrastructure commands
> + */
> +static int lan75xx_phy_gig_workaround(struct usb_device *udev,
> +                                     struct ueth_data *dev)
> +{
> +       int ret = 0;
> +
> +       /* Only internal phy */
> +       /* Set the phy in Gig loopback */
> +       lan7x_mdio_write(udev, dev->phy_id, MII_BMCR,
> +                        (BMCR_LOOPBACK | BMCR_SPEED1000));
> +
> +       /* Wait for the link up */
> +       ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
> +                                     dev->phy_id, MII_BMSR, BMSR_LSTATUS,
> +                                     true, PHY_CONNECT_TIMEOUT_MS);
> +       if (ret)
> +               return ret;
> +
> +       /* phy reset */
> +       ret = lan7x_pmt_phy_reset(udev, dev);
> +       return ret;

Just return lan7x_pmt_phy_reset(udev, dev);

> +}
> +
> +static int lan75xx_update_flowcontrol(struct usb_device *udev,
> +                                     struct ueth_data *dev)
> +{
> +       uint32_t flow = 0, fct_flow = 0;
> +       int ret;
> +
> +       ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, FLOW, flow);

       if (ret)
              return ret;

> +       ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow);
> +       return ret;

Return directly

> +}
> +
> +static int lan75xx_read_mac(unsigned char *enetaddr,
> +                           struct usb_device *udev)
> +{
> +       /*
> +        * Refer to the doc/README.enetaddr and doc/README.usb for
> +        * the U-Boot MAC address policy
> +        */
> +       return lan7x_read_eeprom_mac(enetaddr, udev);
> +}
> +
> +static int lan75xx_write_hwaddr_common(struct usb_device *udev,
> +                                      struct lan7x_private *priv,
> +                                      unsigned char *enetaddr)
> +{
> +       u32 addr_lo = get_unaligned_le32(&enetaddr[0]);
> +       u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]);
> +       int ret;
> +
> +       /* set hardware address */
> +       ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX + 4, addr_lo);
> +       if (ret)
> +               return ret;
> +
> +       addr_hi |= LAN75XX_ADDR_FILTX_FB_VALID;
> +       ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX, addr_hi);
> +       if (ret)
> +               return ret;
> +
> +       debug("MAC addr %pM written\n", enetaddr);
> +       priv->have_hwaddr = 1;
> +
> +       return 0;
> +}
> +
> +static int lan75xx_set_multicast(struct usb_device *udev)
> +{
> +       int ret;
> +       u32 write_buf;
> +
> +       /* No multicast in u-boot */

May want to... will enable IPv6 later.

> +       write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
> +       ret = lan7x_write_reg(udev, LAN75XX_RFE_CTL, write_buf);
> +
> +       return ret;
> +}
> +
> +/* starts the TX path */
> +static void lan75xx_start_tx_path(struct usb_device *udev)
> +{
> +       u32 reg_val;
> +
> +       /* Enable Tx at MAC */
> +       reg_val = MAC_TX_TXEN;

Why not just pass it into the function directly? Applies globally when
the assignment is a single mask.

> +       lan7x_write_reg(udev, MAC_TX, reg_val);
> +
> +       /* Enable Tx at SCSRs */
> +       reg_val = FCT_TX_CTL_EN;
> +       lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, reg_val);
> +}
> +
> +/* Starts the Receive path */
> +static void lan75xx_start_rx_path(struct usb_device *udev)
> +{
> +       u32 reg_val;
> +
> +       /* Enable Rx at MAC */
> +       reg_val = LAN7X_MAC_RX_MAX_SIZE_DEFAULT |
> +               MAC_RX_FCS_STRIP | MAC_RX_RXEN;
> +       lan7x_write_reg(udev, MAC_RX, reg_val);
> +
> +       /* Enable Rx at SCSRs */
> +       reg_val = FCT_RX_CTL_EN;
> +       lan7x_write_reg(udev, LAN75XX_FCT_RX_CTL, reg_val);
> +}
> +
> +static int lan75xx_basic_reset(struct usb_device *udev,
> +                              struct ueth_data *dev,
> +                              struct lan7x_private *priv)
> +{
> +       int ret;
> +       u32 write_buf;
> +
> +       ret = lan7x_basic_reset(udev, dev);
> +       if (ret)
> +               return ret;
> +
> +       /* Keep the chip ID */
> +       ret = lan7x_read_reg(udev, ID_REV, &priv->chipid);
> +       if (ret)
> +               return ret;
> +       debug("LAN75xx ID_REV = 0x%08x\n", priv->chipid);
> +
> +       priv->chipid = (priv->chipid & ID_REV_CHIP_ID_MASK) >> 16;
> +
> +       /* Respond to the IN token with a NAK */
> +       ret = lan7x_read_reg(udev, HW_CFG, &write_buf);
> +       if (ret)
> +               return ret;
> +       write_buf |= LAN75XX_HW_CFG_BIR;
> +       ret = lan7x_write_reg(udev, HW_CFG, write_buf);
> +
> +       return ret;
> +}
> +
> +static int lan75xx_init_common(struct usb_device *udev,
> +                              struct ueth_data *dev,
> +                              struct lan7x_private *priv,
> +                              unsigned char *enetaddr)
> +{
> +       int ret;
> +       u32 write_buf;
> +
> +       /* Reset and read Mac addr were done in get_info() or in probe() */
> +
> +#ifndef CONFIG_DM_ETH
> +       if (!priv->have_hwaddr && is_valid_ethaddr(enetaddr)) {
> +               priv->have_hwaddr = 1;
> +               debug("LAN75xx: MAC address found and set %pM\n", enetaddr);
> +       }
> +#endif
> +       if (!priv->have_hwaddr) {
> +               printf("Error: LAN75xx: No MAC address set - set usbethaddr\n");
> +               return -EADDRNOTAVAIL;
> +       }
> +       ret = lan75xx_write_hwaddr_common(udev, priv, enetaddr);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN75XX_BURST_CAP, 0);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN75XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY);
> +       if (ret)
> +               return ret;
> +
> +       /* set FIFO sizes */
> +       write_buf = (MAX_RX_FIFO_SIZE - 512) / 512;
> +       ret = lan7x_write_reg(udev, LAN75XX_FCT_RX_FIFO_END, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       write_buf = (MAX_TX_FIFO_SIZE - 512) / 512;
> +       ret = lan7x_write_reg(udev, LAN75XX_FCT_TX_FIFO_END, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       /* Init Tx */
> +       ret = lan7x_write_reg(udev, FLOW, 0);
> +       if (ret)
> +               return ret;
> +
> +       /* Init Rx. Set Vlan, keep defult for VLAN on 75xx */
> +       ret = lan75xx_set_multicast(udev);
> +       if (ret)
> +               return ret;
> +
> +       /* phy workaround for gig link */
> +       ret = lan75xx_phy_gig_workaround(udev, dev);
> +       if (ret)
> +               return ret;
> +
> +       /* Init PHY, autonego, and link */
> +       ret = lan7x_phy_initialize(udev, dev);
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * MAC_CR has to be set after PHY init.
> +        * MAC will auto detect the PHY speed.
> +        */
> +       ret = lan7x_read_reg(udev, MAC_CR, &write_buf);
> +       if (ret)
> +               return ret;
> +       write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP;
> +       ret = lan7x_write_reg(udev, MAC_CR, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       lan75xx_start_tx_path(udev);
> +       lan75xx_start_rx_path(udev);
> +
> +       ret = lan75xx_update_flowcontrol(udev, dev);
> +
> +       return ret;
> +}
> +
> +#ifndef CONFIG_DM_ETH
> +/*
> + * lan75xx callbacks
> + */
> +static int lan75xx_init(struct eth_device *eth, bd_t *bd)
> +{
> +       struct ueth_data *dev = (struct ueth_data *)eth->priv;
> +       struct usb_device *udev = dev->pusb_dev;
> +       struct lan7x_private *priv = (struct lan7x_private *)dev->dev_priv;
> +
> +       return lan75xx_init_common(udev, dev, priv, eth->enetaddr);
> +}
> +
> +static int lan75xx_write_hwaddr(struct eth_device *eth)
> +{
> +       struct ueth_data *dev = eth->priv;
> +       struct usb_device *udev = dev->pusb_dev;
> +       struct lan7x_private *priv = dev->dev_priv;
> +
> +       return lan75xx_write_hwaddr_common(udev, priv, eth->enetaddr);
> +}
> +
> +/*
> + * Microchip probing functions
> + */
> +void lan75xx_eth_before_probe(void)
> +{
> +       curr_eth_dev = 0;
> +}
> +
> +static const struct lan7x_dongle lan75xx_dongles[] = {
> +       {0x0424, 0x7500},       /* LAN7500 USB Ethernet */
> +       {0x0000, 0x0000}        /* END - Do not remove */
> +};
> +
> +/* Probe to see if a new device is actually an Microchip device */
> +int lan75xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +                     struct ueth_data *ss)
> +{
> +       int i;
> +
> +       /* let's examine the device now */
> +       for (i = 0; lan75xx_dongles[i].vendor != 0; i++) {
> +               if (dev->descriptor.idVendor == lan75xx_dongles[i].vendor &&
> +                   dev->descriptor.idProduct == lan75xx_dongles[i].product)
> +                       /* Found a supported dongle */
> +                       break;
> +       }
> +       if (lan75xx_dongles[i].vendor == 0)
> +               return 0;
> +
> +       /* At this point, we know we've got a live one */
> +       debug("\n\nUSB Ethernet device LAN75xx detected\n");
> +
> +       /*
> +        * Note that this function needs to return 1
> +        * for success
> +        */
> +       return lan7x_eth_probe(dev, ifnum, ss);
> +}
> +
> +int lan75xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
> +                        struct eth_device *eth)
> +{
> +       struct lan7x_private *priv = ss->dev_priv;
> +
> +       printf("LAN75xx name: %s%d\n", LAN75XX_BASE_NAME, curr_eth_dev);
> +
> +       if (!eth) {
> +               debug("%s: missing parameter.\n", __func__);
> +               return 0;
> +       }
> +       sprintf(eth->name, "%s%d", LAN75XX_BASE_NAME, curr_eth_dev++);
> +       eth->init = lan75xx_init;
> +       eth->send = lan7x_send;
> +       eth->recv = lan7x_recv;
> +       eth->halt = lan7x_halt;
> +       eth->write_hwaddr = lan75xx_write_hwaddr;
> +       eth->priv = ss;
> +
> +       /* Do a reset in order to get the MAC address from HW */
> +       if (lan75xx_basic_reset(dev, ss, priv))
> +               return 0;
> +
> +       /* Get the MAC address */
> +       /*
> +        * We must set the eth->enetaddr from HW because the upper layer
> +        * will force to use the environmental var (usbethaddr) or random if
> +        * there is no valid MAC address in eth->enetaddr.
> +        */
> +       lan75xx_read_mac(eth->enetaddr, dev);
> +       /* Do not return 0 for not finding MAC addr in HW */
> +
> +       return 1;
> +}
> +#endif /* !CONFIG_DM_ETH */
> +
> +#ifdef CONFIG_DM_ETH
> +static int lan75xx_eth_start(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +
> +       /* Driver-model Ethernet ensures we have this */
> +       priv->have_hwaddr = 1;
> +
> +       return lan75xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr);
> +}
> +
> +int lan75xx_write_hwaddr(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +
> +       return lan75xx_write_hwaddr_common(udev, priv, pdata->enetaddr);
> +}
> +
> +int lan75xx_read_rom_hwaddr(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +       int ret;
> +
> +       ret = lan75xx_read_mac(pdata->enetaddr, udev);
> +       if (ret)
> +               memset(pdata->enetaddr, 0, 6);
> +
> +       return 0;
> +}
> +
> +static int lan75xx_eth_probe(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +       struct ueth_data *ueth = &priv->ueth;
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +
> +       /* Do a reset in order to get the MAC address from HW */
> +       if (lan75xx_basic_reset(udev, ueth, priv))
> +               return 0;
> +
> +       /* Get the MAC address */
> +       /*
> +        * We must set the eth->enetaddr from HW because the upper layer
> +        * will force to use the environmental var (usbethaddr) or random if
> +        * there is no valid MAC address in eth->enetaddr.
> +        */
> +       lan75xx_read_mac(pdata->enetaddr, udev);
> +       /* Do not return 0 for not finding MAC addr in HW */
> +
> +       return usb_ether_register(dev, ueth, RX_URB_SIZE);
> +}

I agree that these can all be squashed to remove non-DM support and
move all of the common functions up into these DM functions.

> +
> +static const struct eth_ops lan75xx_eth_ops = {
> +       .start  = lan75xx_eth_start,
> +       .send   = lan7x_eth_send,
> +       .recv   = lan7x_eth_recv,
> +       .free_pkt = lan7x_free_pkt,
> +       .stop   = lan7x_eth_stop,
> +       .write_hwaddr = lan75xx_write_hwaddr,
> +       .read_rom_hwaddr = lan75xx_read_rom_hwaddr,
> +};
> +
> +U_BOOT_DRIVER(lan75xx_eth) = {
> +       .name   = "lan75xx_eth",
> +       .id     = UCLASS_ETH,
> +       .probe  = lan75xx_eth_probe,
> +       .ops    = &lan75xx_eth_ops,
> +       .priv_auto_alloc_size = sizeof(struct lan7x_private),
> +       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
> +};
> +
> +static const struct usb_device_id lan75xx_eth_id_table[] = {
> +       { USB_DEVICE(0x0424, 0x7500) }, /* LAN7500 USB Ethernet */
> +       { }             /* Terminating entry */
> +};
> +
> +U_BOOT_USB_DEVICE(lan75xx_eth, lan75xx_eth_id_table);
> +#endif
> diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c
> new file mode 100644
> index 0000000..e450f2c
> --- /dev/null
> +++ b/drivers/usb/eth/lan78xx.c
> @@ -0,0 +1,611 @@
> +/*
> + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <dm.h>
> +#include <usb.h>
> +#include "usb_ether.h"
> +#include "lan7x.h"
> +
> +/* LAN78xx specific register/bit defines */
> +#define LAN78XX_HW_CFG_LED1_EN         BIT(21) /* Muxed with EEDO */
> +#define LAN78XX_HW_CFG_LED0_EN         BIT(20) /* Muxed with EECLK */
> +
> +#define LAN78XX_USB_CFG0               0x080
> +#define LAN78XX_USB_CFG0_BIR           BIT(6)
> +
> +#define LAN78XX_BURST_CAP              0x090
> +
> +#define LAN78XX_BULK_IN_DLY            0x094
> +
> +#define LAN78XX_RFE_CTL                        0x0B0
> +
> +#define LAN78XX_FCT_RX_CTL             0x0C0
> +
> +#define LAN78XX_FCT_TX_CTL             0x0C4
> +
> +#define LAN78XX_FCT_RX_FIFO_END                0x0C8
> +
> +#define LAN78XX_FCT_TX_FIFO_END                0x0CC
> +
> +#define LAN78XX_FCT_FLOW               0x0D0
> +
> +#define LAN78XX_MAF_BASE               0x400
> +#define LAN78XX_MAF_HIX                        0x00
> +#define LAN78XX_MAF_LOX                        0x04
> +#define LAN78XX_MAF_HI_BEGIN           (LAN78XX_MAF_BASE + LAN78XX_MAF_HIX)
> +#define LAN78XX_MAF_LO_BEGIN           (LAN78XX_MAF_BASE + LAN78XX_MAF_LOX)
> +#define LAN78XX_MAF_HI(index)          (LAN78XX_MAF_BASE + (8 * (index)) + \
> +                                       (LAN78XX_MAF_HIX))
> +#define LAN78XX_MAF_LO(index)          (LAN78XX_MAF_BASE + (8 * (index)) + \
> +                                       (LAN78XX_MAF_LOX))
> +#define LAN78XX_MAF_HI_VALID           BIT(31)
> +
> +/* OTP registers */
> +#define LAN78XX_OTP_BASE_ADDR          0x00001000
> +
> +#define LAN78XX_OTP_PWR_DN             (LAN78XX_OTP_BASE_ADDR + 4 * 0x00)
> +#define LAN78XX_OTP_PWR_DN_PWRDN_N     BIT(0)
> +
> +#define LAN78XX_OTP_ADDR1              (LAN78XX_OTP_BASE_ADDR + 4 * 0x01)
> +#define LAN78XX_OTP_ADDR1_15_11                0x1F
> +
> +#define LAN78XX_OTP_ADDR2              (LAN78XX_OTP_BASE_ADDR + 4 * 0x02)
> +#define LAN78XX_OTP_ADDR2_10_3         0xFF
> +
> +#define LAN78XX_OTP_RD_DATA            (LAN78XX_OTP_BASE_ADDR + 4 * 0x06)
> +
> +#define LAN78XX_OTP_FUNC_CMD           (LAN78XX_OTP_BASE_ADDR + 4 * 0x08)
> +#define LAN78XX_OTP_FUNC_CMD_READ      BIT(0)
> +
> +#define LAN78XX_OTP_CMD_GO             (LAN78XX_OTP_BASE_ADDR + 4 * 0x0A)
> +#define LAN78XX_OTP_CMD_GO_GO          BIT(0)
> +
> +#define LAN78XX_OTP_STATUS             (LAN78XX_OTP_BASE_ADDR + 4 * 0x0C)
> +#define LAN78XX_OTP_STATUS_BUSY                BIT(0)
> +
> +#define LAN78XX_OTP_INDICATOR_1                0xF3
> +#define LAN78XX_OTP_INDICATOR_2                0xF7
> +
> +#ifndef CONFIG_DM_ETH
> +/* local vars */
> +static int curr_eth_dev;       /* index for name of next device detected */
> +
> +/* local defines */
> +#define LAN78XX_BASE_NAME "lan78xx"
> +#endif
> +
> +/*
> + * Lan78xx infrastructure commands
> + */
> +static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset,
> +                               u32 length, u8 *data)
> +{
> +       int i;
> +       int ret;
> +       u32 buf;
> +
> +       ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf);
> +       if (ret)
> +               return ret;
> +
> +       if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) {
> +               /* clear it and wait to be cleared */
> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0);

Either you don't care about the ret value, in which case why is there
one, or you are losing it by overwriting it on the next call. You
should probably be checking it after every assignment. Applies
globally.

> +               ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_PWR_DN_PWRDN_N",
> +                                        LAN78XX_OTP_PWR_DN,
> +                                        LAN78XX_OTP_PWR_DN_PWRDN_N,
> +                                        false, 1000);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       for (i = 0; i < length; i++) {
> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1,
> +                                     ((offset + i) >> 8) &
> +                                     LAN78XX_OTP_ADDR1_15_11);
> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2,
> +                                     ((offset + i) & LAN78XX_OTP_ADDR2_10_3));
> +
> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD,
> +                                     LAN78XX_OTP_FUNC_CMD_READ);
> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO,
> +                                     LAN78XX_OTP_CMD_GO_GO);
> +
> +               if (ret)
> +                       return ret;
> +
> +               ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY",
> +                                        LAN78XX_OTP_STATUS,
> +                                        LAN78XX_OTP_STATUS_BUSY,
> +                                        false, 1000);
> +               if (ret)
> +                       return ret;
> +
> +               ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf);
> +
> +               data[i] = (u8)(buf & 0xFF);
> +       }
> +
> +       return 0;
> +}
> +
> +static int lan78xx_read_otp(struct usb_device *udev, u32 offset,
> +                           u32 length, u8 *data)
> +{
> +       u8 sig;
> +       int ret;
> +
> +       ret = lan78xx_read_raw_otp(udev, 0, 1, &sig);
> +
> +       if (!ret) {
> +               if (sig == LAN78XX_OTP_INDICATOR_1)
> +                       offset = offset;
> +               else if (sig == LAN78XX_OTP_INDICATOR_2)
> +                       offset += 0x100;
> +               else
> +                       return -EINVAL;
> +               ret = lan78xx_read_raw_otp(udev, offset, length, data);
> +       }
> +       debug("LAN78x: MAC address from OTP = %pM\n", data);
> +
> +       return ret;
> +}
> +
> +static int lan78xx_read_otp_mac(unsigned char *enetaddr,
> +                               struct usb_device *udev)
> +{
> +       int ret;
> +
> +       memset(enetaddr, 0, 6);
> +
> +       ret = lan78xx_read_otp(udev,
> +                              EEPROM_MAC_OFFSET,
> +                              ETH_ALEN,
> +                              enetaddr);
> +       if (!ret && is_valid_ethaddr(enetaddr)) {
> +               /* eeprom values are valid so use them */
> +               debug("MAC address read from OTP %pM\n", enetaddr);
> +               return 0;
> +       }
> +       debug("MAC address read from OTP invalid %pM\n", enetaddr);
> +
> +       memset(enetaddr, 0, 6);
> +       return -1;
> +}
> +
> +static int lan78xx_update_flowcontrol(struct usb_device *udev,
> +                                     struct ueth_data *dev)
> +{
> +       uint32_t flow = 0, fct_flow = 0;
> +       int ret;
> +
> +       ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, FLOW, flow);
> +       ret = lan7x_write_reg(udev, LAN78XX_FCT_FLOW, fct_flow);
> +       return ret;
> +}
> +
> +static int lan78xx_read_mac(unsigned char *enetaddr,
> +                           struct usb_device *udev,
> +                           struct lan7x_private *priv)
> +{
> +       u32 val;
> +       int ret;
> +       int saved = 0, done = 0;
> +
> +       /*
> +        * Depends on chip, some EEPROM pins are muxed with LED function.
> +        * disable & restore LED function to access EEPROM.
> +        */
> +       if ((priv->chipid == ID_REV_CHIP_ID_7800) ||
> +           (priv->chipid == ID_REV_CHIP_ID_7850)) {
> +               ret = lan7x_read_reg(udev, HW_CFG, &val);
> +               saved = val;
> +               val &= ~(LAN78XX_HW_CFG_LED1_EN | LAN78XX_HW_CFG_LED0_EN);
> +               ret = lan7x_write_reg(udev, HW_CFG, val);
> +               if (ret)
> +                       goto restore;
> +       }
> +
> +       /*
> +        * Refer to the doc/README.enetaddr and doc/README.usb for
> +        * the U-Boot MAC address policy
> +        */
> +       /* try reading mac address from EEPROM, then from OTP */
> +       ret = lan7x_read_eeprom_mac(enetaddr, udev);
> +       if (!ret)
> +               done = 1;
> +
> +restore:
> +       if ((priv->chipid == ID_REV_CHIP_ID_7800) ||
> +           (priv->chipid == ID_REV_CHIP_ID_7850)) {
> +               ret = lan7x_write_reg(udev, HW_CFG, saved);
> +       }
> +       /* if the EEPROM mac address is good, then exit */
> +       if (done)
> +               return 0;
> +
> +       /* try reading mac address from OTP if the device is LAN78xx */
> +       ret = lan78xx_read_otp_mac(enetaddr, udev);
> +
> +       return ret;
> +}
> +
> +static int lan78xx_write_hwaddr_common(struct usb_device *udev,
> +                                      struct lan7x_private *priv,
> +                                      unsigned char *enetaddr)
> +{
> +       u32 addr_lo = get_unaligned_le32(&enetaddr[0]);
> +       u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]);
> +       int ret;
> +
> +       /* set hardware address */
> +       ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN78XX_MAF_LO(0), addr_lo);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN78XX_MAF_HI(0),
> +                             addr_hi | LAN78XX_MAF_HI_VALID);
> +       if (ret)
> +               return ret;
> +
> +       debug("MAC addr %pM written\n", enetaddr);
> +       priv->have_hwaddr = 1;
> +
> +       return 0;
> +}
> +
> +static int lan78xx_set_multicast(struct usb_device *udev)
> +{
> +       int ret;
> +       u32 write_buf;
> +
> +       /* No multicast in u-boot */
> +       write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
> +       ret = lan7x_write_reg(udev, LAN78XX_RFE_CTL, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +/* starts the TX path */
> +static void lan78xx_start_tx_path(struct usb_device *udev)
> +{
> +       u32 reg_val;
> +
> +       /* Enable Tx at MAC */
> +       reg_val = MAC_TX_TXEN;
> +       lan7x_write_reg(udev, MAC_TX, reg_val);
> +
> +       /* Enable Tx at SCSRs */
> +       reg_val = FCT_TX_CTL_EN;
> +       lan7x_write_reg(udev, LAN78XX_FCT_TX_CTL, reg_val);
> +}
> +
> +/* Starts the Receive path */
> +static void lan78xx_start_rx_path(struct usb_device *udev)
> +{
> +       u32 reg_val;
> +
> +       /* Enable Rx at MAC */
> +       reg_val = LAN7X_MAC_RX_MAX_SIZE_DEFAULT |
> +               MAC_RX_FCS_STRIP | MAC_RX_RXEN;
> +       lan7x_write_reg(udev, MAC_RX, reg_val);
> +
> +       /* Enable Rx at SCSRs */
> +       reg_val = FCT_RX_CTL_EN;
> +       lan7x_write_reg(udev, LAN78XX_FCT_RX_CTL, reg_val);
> +}
> +
> +static int lan78xx_basic_reset(struct usb_device *udev,
> +                              struct ueth_data *dev,
> +                              struct lan7x_private *priv)
> +{
> +       int ret;
> +       u32 write_buf;
> +
> +       ret = lan7x_basic_reset(udev, dev);
> +       if (ret)
> +               return ret;
> +
> +       /* Keep the chip ID */
> +       ret = lan7x_read_reg(udev, ID_REV, &priv->chipid);
> +       if (ret)
> +               return ret;
> +       debug("LAN78xx ID_REV = 0x%08x\n", priv->chipid);
> +
> +       priv->chipid = (priv->chipid & ID_REV_CHIP_ID_MASK) >> 16;
> +
> +       /* Respond to the IN token with a NAK */
> +       ret = lan7x_read_reg(udev, LAN78XX_USB_CFG0, &write_buf);
> +       if (ret)
> +               return ret;
> +       write_buf |= LAN78XX_USB_CFG0_BIR;
> +       ret = lan7x_write_reg(udev, LAN78XX_USB_CFG0, write_buf);
> +
> +       return ret;
> +}
> +
> +static int lan78xx_init_common(struct usb_device *udev,
> +                              struct ueth_data *dev,
> +                              struct lan7x_private *priv,
> +                              unsigned char *enetaddr)
> +{
> +       int ret;
> +       u32 write_buf;
> +
> +       /* Reset and read Mac addr were done in get_info() or in probe() */
> +
> +#ifndef CONFIG_DM_ETH
> +       if (!priv->have_hwaddr && is_valid_ethaddr(enetaddr)) {
> +               priv->have_hwaddr = 1;
> +               debug("LAN78xx: MAC address found and set %pM\n", enetaddr);
> +       }
> +#endif
> +       if (!priv->have_hwaddr) {
> +               printf("Error: LAN78xx: No MAC address set - set usbethaddr\n");
> +               return -EADDRNOTAVAIL;
> +       }
> +       ret = lan78xx_write_hwaddr_common(udev, priv, enetaddr);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN78XX_BURST_CAP, 0);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, LAN78XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF);
> +       if (ret)
> +               return ret;
> +
> +       /* set FIFO sizes */
> +       write_buf = (MAX_RX_FIFO_SIZE - 512) / 512;
> +       ret = lan7x_write_reg(udev, LAN78XX_FCT_RX_FIFO_END, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       write_buf = (MAX_TX_FIFO_SIZE - 512) / 512;
> +       ret = lan7x_write_reg(udev, LAN78XX_FCT_TX_FIFO_END, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       /* Init Tx */
> +       ret = lan7x_write_reg(udev, FLOW, 0);
> +       if (ret)
> +               return ret;
> +
> +       /* Init Rx. Set Vlan, keep defult for VLAN on 78xx */
> +       ret = lan78xx_set_multicast(udev);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Init PHY, autonego, and link */
> +       ret = lan7x_phy_initialize(udev, dev);
> +       if (ret < 0)
> +               return ret;
> +
> +       /*
> +        * MAC_CR has to be set after PHY init.
> +        * MAC will auto detect the PHY speed.
> +        */
> +       ret = lan7x_read_reg(udev, MAC_CR, &write_buf);
> +       if (ret)
> +               return ret;
> +       write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP;
> +       ret = lan7x_write_reg(udev, MAC_CR, write_buf);
> +       if (ret)
> +               return ret;
> +
> +       lan78xx_start_tx_path(udev);
> +       lan78xx_start_rx_path(udev);
> +
> +       ret = lan78xx_update_flowcontrol(udev, dev);
> +
> +       return ret;
> +}
> +
> +#ifndef CONFIG_DM_ETH
> +/*
> + * lan78xx callbacks
> + */
> +static int lan78xx_init(struct eth_device *eth, bd_t *bd)
> +{
> +       struct ueth_data *dev = (struct ueth_data *)eth->priv;
> +       struct usb_device *udev = dev->pusb_dev;
> +       struct lan7x_private *priv = (struct lan7x_private *)dev->dev_priv;
> +
> +       return lan78xx_init_common(udev, dev, priv, eth->enetaddr);
> +}
> +
> +static int lan78xx_write_hwaddr(struct eth_device *eth)
> +{
> +       struct ueth_data *dev = eth->priv;
> +       struct usb_device *udev = dev->pusb_dev;
> +       struct lan7x_private *priv = dev->dev_priv;
> +
> +       return lan78xx_write_hwaddr_common(udev, priv, eth->enetaddr);
> +}
> +
> +/*
> + * Microchip probing functions
> + */
> +void lan78xx_eth_before_probe(void)
> +{
> +       curr_eth_dev = 0;
> +}
> +
> +static const struct lan7x_dongle lan78xx_dongles[] = {
> +       {0x0424, 0x7800},       /* LAN7800 USB Ethernet */
> +       {0x0424, 0x7850},       /* LAN7850 USB Ethernet */
> +       {0x0000, 0x0000}        /* END - Do not remove */
> +};
> +
> +/* Probe to see if a new device is actually an Microchip device */
> +int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +                     struct ueth_data *ss)
> +{
> +       int i;
> +
> +       /* let's examine the device now */
> +
> +       for (i = 0; lan78xx_dongles[i].vendor != 0; i++) {
> +               if (dev->descriptor.idVendor == lan78xx_dongles[i].vendor &&
> +                   dev->descriptor.idProduct == lan78xx_dongles[i].product)
> +                       /* Found a supported dongle */
> +                       break;
> +       }
> +       if (lan78xx_dongles[i].vendor == 0)
> +               return 0;
> +
> +       /* At this point, we know we've got a live one */
> +       debug("\n\nUSB Ethernet device LAN78xx detected\n");
> +
> +       /*
> +        * Note that this function needs to return 1
> +        * for success
> +        */
> +       return lan7x_eth_probe(dev, ifnum, ss);
> +}
> +
> +int lan78xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
> +                        struct eth_device *eth)
> +{
> +       struct lan7x_private *priv = ss->dev_priv;
> +
> +       printf("LAN78xx name: %s%d\n", LAN78XX_BASE_NAME, curr_eth_dev);
> +
> +       if (!eth) {
> +               debug("%s: missing parameter.\n", __func__);
> +               return 0;
> +       }
> +       sprintf(eth->name, "%s%d", LAN78XX_BASE_NAME, curr_eth_dev++);
> +       eth->init = lan78xx_init;
> +       eth->send = lan7x_send;
> +       eth->recv = lan7x_recv;
> +       eth->halt = lan7x_halt;
> +       eth->write_hwaddr = lan78xx_write_hwaddr;
> +       eth->priv = ss;
> +
> +       /* Do a reset in order to get the MAC address from HW */
> +       if (lan78xx_basic_reset(dev, ss, priv))
> +               return 0;
> +
> +       /* Get the MAC address */
> +       /*
> +        * We must set the eth->enetaddr from HW because the upper layer
> +        * will force to use the environmental var (usbethaddr) or random if
> +        * there is no valid MAC address in eth->enetaddr.
> +        */
> +       lan78xx_read_mac(eth->enetaddr, dev, priv);
> +       /* Do not return 0 for not finding MAC addr in HW */
> +
> +       return 1;
> +}
> +#endif /* !CONFIG_DM_ETH */
> +
> +#ifdef CONFIG_DM_ETH
> +static int lan78xx_eth_start(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +
> +       /* Driver-model Ethernet ensures we have this */
> +       priv->have_hwaddr = 1;
> +
> +       return lan78xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr);
> +}
> +
> +int lan78xx_write_hwaddr(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +
> +       return lan78xx_write_hwaddr_common(udev, priv, pdata->enetaddr);
> +}
> +
> +int lan78xx_read_rom_hwaddr(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +       int ret;
> +
> +       ret = lan78xx_read_mac(pdata->enetaddr, udev, priv);
> +       if (ret)
> +               memset(pdata->enetaddr, 0, 6);
> +
> +       return 0;
> +}
> +
> +static int lan78xx_eth_probe(struct udevice *dev)
> +{
> +       struct usb_device *udev = dev_get_parent_priv(dev);
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +       struct ueth_data *ueth = &priv->ueth;
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +
> +       /* Do a reset in order to get the MAC address from HW */
> +       if (lan78xx_basic_reset(udev, ueth, priv))
> +               return 0;
> +
> +       /* Get the MAC address */
> +       /*
> +        * We must set the eth->enetaddr from HW because the upper layer
> +        * will force to use the environmental var (usbethaddr) or random if
> +        * there is no valid MAC address in eth->enetaddr.
> +        */
> +       lan78xx_read_mac(pdata->enetaddr, udev, priv);
> +       /* Do not return 0 for not finding MAC addr in HW */
> +
> +       return usb_ether_register(dev, ueth, RX_URB_SIZE);
> +}
> +
> +static const struct eth_ops lan78xx_eth_ops = {
> +       .start  = lan78xx_eth_start,
> +       .send   = lan7x_eth_send,
> +       .recv   = lan7x_eth_recv,
> +       .free_pkt = lan7x_free_pkt,
> +       .stop   = lan7x_eth_stop,
> +       .write_hwaddr = lan78xx_write_hwaddr,
> +       .read_rom_hwaddr = lan78xx_read_rom_hwaddr,
> +};
> +
> +U_BOOT_DRIVER(lan78xx_eth) = {
> +       .name   = "lan78xx_eth",
> +       .id     = UCLASS_ETH,
> +       .probe  = lan78xx_eth_probe,
> +       .ops    = &lan78xx_eth_ops,
> +       .priv_auto_alloc_size = sizeof(struct lan7x_private),
> +       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
> +};
> +
> +static const struct usb_device_id lan78xx_eth_id_table[] = {
> +       { USB_DEVICE(0x0424, 0x7800) }, /* LAN7800 USB Ethernet */
> +       { USB_DEVICE(0x0424, 0x7850) }, /* LAN7850 USB Ethernet */
> +       { }             /* Terminating entry */
> +};
> +
> +U_BOOT_USB_DEVICE(lan78xx_eth, lan78xx_eth_id_table);
> +#endif
> diff --git a/drivers/usb/eth/lan7x.c b/drivers/usb/eth/lan7x.c
> new file mode 100644
> index 0000000..baa778f
> --- /dev/null
> +++ b/drivers/usb/eth/lan7x.c
> @@ -0,0 +1,680 @@
> +/*
> + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#include <dm.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <usb.h>
> +#include <linux/mii.h>
> +#include <linux/ethtool.h>
> +#include "usb_ether.h"
> +#include "lan7x.h"
> +
> +/*
> + * Lan7x infrastructure commands
> + */
> +int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data)
> +{
> +       int len;
> +       ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
> +
> +       cpu_to_le32s(&data);
> +       tmpbuf[0] = data;
> +
> +       len = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
> +                             USB_VENDOR_REQUEST_WRITE_REGISTER,
> +                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +                             0, index, tmpbuf, sizeof(data),
> +                             USB_CTRL_SET_TIMEOUT_MS);
> +       if (len != sizeof(data)) {
> +               debug("%s failed: index=%d, data=%d, len=%d",
> +                     __func__, index, data, len);
> +               return -EIO;
> +       }
> +       return 0;
> +}
> +
> +int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data)
> +{
> +       int len;
> +       ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
> +
> +       len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
> +                             USB_VENDOR_REQUEST_READ_REGISTER,
> +                             USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +                             0, index, tmpbuf, sizeof(*data),
> +                             USB_CTRL_GET_TIMEOUT_MS);
> +       *data = tmpbuf[0];
> +       if (len != sizeof(*data)) {
> +               debug("%s failed: index=%d, len=%d", __func__, index, len);
> +               return -EIO;
> +       }
> +
> +       le32_to_cpus(data);
> +       return 0;
> +}
> +
> +/* Loop until the read is completed with timeout */
> +int lan7x_wait_for_bit(struct usb_device *udev,
> +                      const char *prefix, const u32 index,
> +                      const u32 mask, const bool set,
> +                      unsigned int timeout_ms)

Can you not use the generic one? include/wait_bit.h

> +{
> +       u32 val;
> +
> +       while (--timeout_ms) {
> +               lan7x_read_reg(udev, index, &val);
> +
> +               if (!set)
> +                       val = ~val;
> +
> +               if ((val & mask) == mask)
> +                       return 0;
> +
> +               mdelay(1);
> +       }
> +
> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
> +             prefix, index, mask, set);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int lan7x_phy_wait_not_busy(struct usb_device *udev)
> +{
> +       return lan7x_wait_for_bit(udev, __func__,
> +                                 MII_ACC, MII_ACC_MII_BUSY,
> +                                 false, 100);
> +}
> +
> +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx)
> +{
> +       u32 val, addr;
> +
> +       /* confirm MII not busy */
> +       if (lan7x_phy_wait_not_busy(udev)) {
> +               debug("MII is busy in %s\n", __func__);
> +               return -ETIMEDOUT;
> +       }
> +
> +       /* set the address, index & direction (read from PHY) */
> +       addr = (phy_id << 11) | (idx << 6) |
> +               MII_ACC_MII_READ | MII_ACC_MII_BUSY;
> +       lan7x_write_reg(udev, MII_ACC, addr);
> +
> +       if (lan7x_phy_wait_not_busy(udev)) {
> +               debug("Timed out reading MII reg %02X\n", idx);
> +               return -ETIMEDOUT;
> +       }
> +
> +       lan7x_read_reg(udev, MII_DATA, &val);
> +
> +       return (u16) (val & 0xFFFF);
> +}
> +
> +int lan7x_mdio_wait_for_bit(struct usb_device *udev,
> +                           const char *prefix,
> +                           int phy_id, const u32 index,
> +                           const u32 mask, const bool set,
> +                           unsigned int timeout_ms)
> +{
> +       u32 val;
> +
> +       while (--timeout_ms) {
> +               val = lan7x_mdio_read(udev, phy_id, index);
> +
> +               if (!set)
> +                       val = ~val;
> +
> +               if ((val & mask) == mask)
> +                       return 0;
> +
> +               mdelay(1);
> +       }
> +
> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
> +             prefix, index, mask, set);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, int regval)
> +{
> +       u32 val, addr;
> +
> +       /* confirm MII not busy */
> +       if (lan7x_phy_wait_not_busy(udev)) {
> +               debug("MII is busy in %s\n", __func__);
> +               return;
> +       }
> +
> +       val = regval;
> +       lan7x_write_reg(udev, MII_DATA, val);
> +
> +       /* set the address, index & direction (write to PHY) */
> +       addr = (phy_id << 11) | (idx << 6) |
> +               MII_ACC_MII_WRITE | MII_ACC_MII_BUSY;
> +       lan7x_write_reg(udev, MII_ACC, addr);
> +
> +       if (lan7x_phy_wait_not_busy(udev))
> +               debug("Timed out writing MII reg %02X\n", idx);
> +}
> +
> +static int lan7x_eeprom_confirm_not_busy(struct usb_device *udev)
> +{
> +       return lan7x_wait_for_bit(udev, __func__,
> +                                 E2P_CMD, E2P_CMD_EPC_BUSY,
> +                                 false, 100);
> +}
> +
> +static int lan7x_wait_eeprom(struct usb_device *udev)
> +{
> +       return lan7x_wait_for_bit(udev, __func__,
> +                                 E2P_CMD,
> +                                 (E2P_CMD_EPC_BUSY | E2P_CMD_EPC_TIMEOUT),
> +                                 false, 100);
> +}
> +
> +static int lan7x_read_eeprom(struct usb_device *udev,
> +                            u32 offset, u32 length, u8 *data)
> +{
> +       u32 val;
> +       int i, ret;
> +
> +       ret = lan7x_eeprom_confirm_not_busy(udev);
> +       if (ret)
> +               return ret;
> +
> +       for (i = 0; i < length; i++) {
> +               val = E2P_CMD_EPC_BUSY | E2P_CMD_EPC_CMD_READ |
> +                       (offset & E2P_CMD_EPC_ADDR_MASK);
> +               lan7x_write_reg(udev, E2P_CMD, val);
> +
> +               ret = lan7x_wait_eeprom(udev);
> +               if (ret)
> +                       return ret;
> +
> +               lan7x_read_reg(udev, E2P_DATA, &val);
> +               data[i] = val & 0xFF;
> +               offset++;
> +       }
> +
> +       return ret;
> +}
> +
> +/*
> + * mii_nway_restart - restart NWay (autonegotiation) for this interface
> + *
> + * Returns 0 on success, negative on error.
> + */
> +static int mii_nway_restart(struct usb_device *udev, struct ueth_data *dev)
> +{
> +       int bmcr;
> +       int r = -1;
> +
> +       /* if autoneg is off, it's an error */
> +       bmcr = lan7x_mdio_read(udev, dev->phy_id, MII_BMCR);
> +
> +       if (bmcr & BMCR_ANENABLE) {
> +               bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
> +               lan7x_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr);
> +               r = 0;
> +       } else {
> +               debug("ERROR! phy autoneg is off. BMCR = 0x%04x\n", bmcr);
> +       }
> +       return r;
> +}
> +
> +int lan7x_phy_initialize(struct usb_device *udev,
> +                        struct ueth_data *dev)
> +{
> +       int r, link_detected;
> +
> +       lan7x_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET);
> +       r = lan7x_mdio_wait_for_bit(udev, "BMCR_RESET",
> +                                   dev->phy_id, MII_BMCR, BMCR_RESET,
> +                                   false, 1000);
> +
> +       lan7x_mdio_write(udev, dev->phy_id, MII_ADVERTISE,
> +                        ADVERTISE_ALL | ADVERTISE_CSMA |
> +                        ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
> +
> +       lan7x_mdio_write(udev, dev->phy_id, MII_CTRL1000,
> +                        ADVERTISE_1000FULL);
> +
> +       r = mii_nway_restart(udev, dev);
> +       r = lan7x_mdio_wait_for_bit(udev, "BMSR_ANEGCOMPLETE",
> +                                   dev->phy_id, MII_BMSR, BMSR_ANEGCOMPLETE,
> +                                   true, PHY_CONNECT_TIMEOUT_MS);
> +
> +       if (r == 0) {
> +               debug("phy initialised succesfully\n");
> +       } else {
> +               debug("phy initialised failed id=%d\n", dev->phy_id);
> +               return r;
> +       }
> +
> +       printf("LAN7x: Waiting for Ethernet connection... ");
> +       r = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
> +                                   dev->phy_id, MII_BMSR, BMSR_LSTATUS,
> +                                   true, PHY_CONNECT_TIMEOUT_MS);
> +
> +       if (r < 0) {
> +               printf("unable to connect.\n");
> +               return -EIO;
> +       }
> +       printf("done.\n");
> +
> +       link_detected = lan7x_mdio_read(udev, dev->phy_id, MII_BMSR);
> +       debug("MII_BMSR=0x%04x\n", link_detected);
> +
> +       return 0;
> +}
> +
> +static int lan7x_mii_get_an(uint32_t advertising_reg)
> +{
> +       int advertising = 0;
> +
> +       if (advertising_reg & LPA_LPACK)
> +               advertising |= ADVERTISED_Autoneg;
> +       if (advertising_reg & ADVERTISE_10HALF)
> +               advertising |= ADVERTISED_10baseT_Half;
> +       if (advertising_reg & ADVERTISE_10FULL)
> +               advertising |= ADVERTISED_10baseT_Full;
> +       if (advertising_reg & ADVERTISE_100HALF)
> +               advertising |= ADVERTISED_100baseT_Half;
> +       if (advertising_reg & ADVERTISE_100FULL)
> +               advertising |= ADVERTISED_100baseT_Full;
> +
> +       return advertising;
> +}
> +
> +int lan7x_update_flowcontrol(struct usb_device *udev,
> +                            struct ueth_data *dev,
> +                            uint32_t *flow, uint32_t *fct_flow)
> +{
> +       uint32_t lcladv, rmtadv, ctrl1000, stat1000;
> +       uint32_t advertising = 0, lp_advertising = 0, nego = 0;
> +       uint32_t duplex = 0;
> +       u8 cap = 0;
> +
> +       lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
> +       advertising = lan7x_mii_get_an(lcladv);
> +
> +       rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
> +       lp_advertising = lan7x_mii_get_an(rmtadv);
> +
> +       ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
> +       stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
> +
> +       if (ctrl1000 & ADVERTISE_1000HALF)
> +               advertising |= ADVERTISED_1000baseT_Half;
> +
> +       if (ctrl1000 & ADVERTISE_1000FULL)
> +               advertising |= ADVERTISED_1000baseT_Full;
> +
> +       if (stat1000 & LPA_1000HALF)
> +               lp_advertising |= ADVERTISED_1000baseT_Half;
> +
> +       if (stat1000 & LPA_1000FULL)
> +               lp_advertising |= ADVERTISED_1000baseT_Full;
> +
> +       nego = advertising & lp_advertising;
> +
> +       debug("LAN7x linked at ");
> +
> +       if (nego & (ADVERTISED_1000baseT_Full | ADVERTISED_1000baseT_Half)) {
> +               debug("1000 ");
> +               duplex = !!(nego & ADVERTISED_1000baseT_Full);
> +
> +       } else if (nego & (ADVERTISED_100baseT_Full |
> +                  ADVERTISED_100baseT_Half)) {
> +               debug("100 ");
> +               duplex = !!(nego & ADVERTISED_100baseT_Full);
> +       } else {
> +               debug("10 ");
> +               duplex = !!(nego & ADVERTISED_10baseT_Full);
> +       }
> +
> +       if (duplex == DUPLEX_FULL)
> +               debug("full dup ");
> +       else
> +               debug("half dup ");
> +
> +       if (duplex == DUPLEX_FULL) {
> +               if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
> +                       cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
> +               } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
> +                       if (lcladv & ADVERTISE_PAUSE_CAP)
> +                               cap = FLOW_CTRL_RX;
> +                       else if (rmtadv & LPA_PAUSE_CAP)
> +                               cap = FLOW_CTRL_TX;
> +               }
> +               debug("TX Flow ");
> +               if (cap & FLOW_CTRL_TX) {
> +                       *flow = (FLOW_CR_TX_FCEN | 0xFFFF);
> +                       /* set fct_flow thresholds to 20% and 80% */
> +                       *fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
> +                                       & 0x7FUL);
> +                       *fct_flow <<= 8UL;
> +                       *fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
> +                                       & 0x7FUL);
> +                       debug("EN ");
> +               } else {
> +                       debug("DIS ");
> +               }
> +               debug("RX Flow ");
> +               if (cap & FLOW_CTRL_RX) {
> +                       *flow |= FLOW_CR_RX_FCEN;
> +                       debug("EN");
> +               } else {
> +                       debug("DIS");
> +               }
> +       }
> +       debug("\n");
> +       return 0;
> +}

I see where Marek is coming from wrt thisall being in phylib already.
I guess you always have a fixed phy internal, so there's no need of
the flexibility of phylib. Maybe there's at least opportunity to
consolidate subroutines even if not using phylib the normal way.

> +
> +int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev)
> +{
> +       int ret;
> +
> +       memset(enetaddr, 0, 6);
> +
> +       ret = lan7x_read_eeprom(udev, 0, 1, enetaddr);
> +
> +       if ((ret == 0) && (enetaddr[0] == EEPROM_INDICATOR)) {
> +               ret = lan7x_read_eeprom(udev,
> +                                       EEPROM_MAC_OFFSET, ETH_ALEN,
> +                                       enetaddr);
> +               if ((ret == 0) && is_valid_ethaddr(enetaddr)) {
> +                       /* eeprom values are valid so use them */
> +                       debug("MAC address read from EEPROM %pM\n",
> +                             enetaddr);
> +                       return 0;
> +               }
> +       }
> +       debug("MAC address read from EEPROM invalid %pM\n", enetaddr);
> +
> +       memset(enetaddr, 0, 6);
> +       return -1;
> +}
> +
> +int lan7x_pmt_phy_reset(struct usb_device *udev,
> +                       struct ueth_data *dev)
> +{
> +       int ret;
> +       u32 data;
> +
> +       ret = lan7x_read_reg(udev, PMT_CTL, &data);
> +       ret = lan7x_write_reg(udev, PMT_CTL, data | PMT_CTL_PHY_RST);
> +       if (ret)
> +               return ret;
> +
> +       /* for LAN7x, we need to check PMT_CTL_READY asserted */
> +       ret = lan7x_wait_for_bit(udev, "PMT_CTL_PHY_RST",
> +                                PMT_CTL, PMT_CTL_PHY_RST,
> +                                false, 1000); /* could take over 125mS */
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_wait_for_bit(udev, "PMT_CTL_READY",
> +                                PMT_CTL, PMT_CTL_READY,
> +                                true, 1000);
> +       return ret;
> +}
> +
> +int lan7x_basic_reset(struct usb_device *udev,
> +                     struct ueth_data *dev)
> +{
> +       int ret;
> +
> +       dev->phy_id = LAN7X_INTERNAL_PHY_ID; /* fixed phy id */
> +
> +       ret = lan7x_write_reg(udev, HW_CFG, HW_CFG_LRST);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_wait_for_bit(udev, "HW_CFG_LRST",
> +                                HW_CFG, HW_CFG_LRST,
> +                                false, 1000);
> +       if (ret)
> +               return ret;
> +
> +       ret = lan7x_pmt_phy_reset(udev, dev);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +int lan7x_send_common(struct ueth_data *dev, void *packet, int length)
> +{
> +       int err;
> +       int actual_len;
> +       u32 tx_cmd_a;
> +       u32 tx_cmd_b;
> +       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg,
> +                                PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b));
> +
> +       debug("** %s(), len %d, buf %#x\n", __func__, length,
> +             (unsigned int)(ulong) msg);
> +       if (length > PKTSIZE)
> +               return -ENOSPC;
> +
> +       /* LAN7x disable all TX offload features for u-boot */
> +       tx_cmd_a = (u32) (length & TX_CMD_A_LEN_MASK) | TX_CMD_A_FCS;
> +       tx_cmd_b = 0;
> +       cpu_to_le32s(&tx_cmd_a);
> +       cpu_to_le32s(&tx_cmd_b);
> +
> +       /* prepend cmd_a and cmd_b */
> +       memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
> +       memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
> +       memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
> +              length);
> +       err = usb_bulk_msg(dev->pusb_dev,
> +                          usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
> +                          (void *)msg,
> +                          length + sizeof(tx_cmd_a) +
> +                          sizeof(tx_cmd_b),
> +                          &actual_len, USB_BULK_SEND_TIMEOUT_MS);
> +       debug("Tx: len = %u, actual = %u, err = %d\n",
> +             (unsigned int)(length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)),
> +             (unsigned int)actual_len, err);
> +
> +       return err;
> +}
> +
> +int lan7x_recv_common(struct ueth_data *dev)
> +{
> +       DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, RX_URB_SIZE);
> +       unsigned char *buf_ptr;
> +       int err;
> +       int actual_len = 0;
> +       u32 packet_len = 0;
> +       u32 rx_cmd_a = 0;
> +
> +       err = usb_bulk_msg(dev->pusb_dev,
> +                          usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
> +                          (void *)recv_buf, RX_URB_SIZE, &actual_len,
> +                          USB_BULK_RECV_TIMEOUT_MS);
> +       if (actual_len == 0) {
> +               debug("Rx: actual_len = 0\n");
> +               return -err;
> +       }
> +
> +       debug("Rx: RX_URB_SIZE = %u, actual = %u, err = %d\n", RX_URB_SIZE,
> +             actual_len, err);
> +       if (err != 0) {
> +               debug("Rx: failed to receive\n");
> +               return -err;
> +       }
> +       if (actual_len > RX_URB_SIZE) {
> +               debug("Rx: received too many bytes %d\n", actual_len);
> +               return -ENOSPC;
> +       }
> +
> +       buf_ptr = recv_buf;
> +
> +       /*
> +        * No multiple Ethernet Frames per USB Packet (MEF) used
> +        * for the U-boot for now.
> +        */
> +       if (actual_len > 0) {
> +               if (actual_len < sizeof(rx_cmd_a)) {
> +                       debug("Rx: incomplete packet length\n");
> +                       return -EIO;
> +               }
> +               memcpy(&rx_cmd_a, buf_ptr, sizeof(rx_cmd_a));
> +               le32_to_cpus(&rx_cmd_a);
> +               if (rx_cmd_a & RX_CMD_A_RXE) {
> +                       debug("Rx: Error header=%#x", rx_cmd_a);
> +                       return -EIO;
> +               }
> +               packet_len = (u16) (rx_cmd_a & RX_CMD_A_LEN_MASK);
> +
> +               if (packet_len > actual_len - sizeof(packet_len)) {
> +                       debug("Rx: too large packet: %d\n", packet_len);
> +                       return -EIO;
> +               }
> +
> +               /*
> +                * For LAN7x, the length in command A does not
> +                * include command A, B, and C length.
> +                * So use it as is.
> +                */
> +
> +               debug("Rx: cmd_a 0x%08X packet_len %d\n", rx_cmd_a, packet_len);
> +
> +               /* Notify net stack */
> +               net_process_received_packet(buf_ptr + 10, packet_len);
> +       }
> +
> +       return err;
> +}
> +
> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +                   struct ueth_data *ss)
> +{
> +       struct usb_interface *iface;
> +       struct usb_interface_descriptor *iface_desc;
> +       int i;
> +
> +       iface = &dev->config.if_desc[ifnum];
> +       iface_desc = &dev->config.if_desc[ifnum].desc;
> +
> +       memset(ss, '\0', sizeof(struct ueth_data));
> +
> +       /* Initialize the ueth_data structure with some useful info */
> +       ss->ifnum = ifnum;
> +       ss->pusb_dev = dev;
> +       ss->subclass = iface_desc->bInterfaceSubClass;
> +       ss->protocol = iface_desc->bInterfaceProtocol;
> +
> +       /*
> +        * We are expecting a minimum of 3 endpoints
> +        * - in, out (bulk), and int.
> +        * We will ignore any others.
> +        */
> +       for (i = 0; i < iface_desc->bNumEndpoints; i++) {
> +               /* is it an BULK endpoint? */
> +               if ((iface->ep_desc[i].bmAttributes &
> +                       USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
> +                       if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
> +                               ss->ep_in =
> +                                       iface->ep_desc[i].bEndpointAddress &
> +                                       USB_ENDPOINT_NUMBER_MASK;
> +                       else
> +                               ss->ep_out =
> +                                       iface->ep_desc[i].bEndpointAddress &
> +                                       USB_ENDPOINT_NUMBER_MASK;
> +               }
> +
> +               /* is it an interrupt endpoint? */
> +               if ((iface->ep_desc[i].bmAttributes &
> +                       USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
> +                       ss->ep_int = iface->ep_desc[i].bEndpointAddress &
> +                               USB_ENDPOINT_NUMBER_MASK;
> +                       ss->irqinterval = iface->ep_desc[i].bInterval;
> +               }
> +       }
> +       debug("Endpoints In %d Out %d Int %d\n",
> +             ss->ep_in, ss->ep_out, ss->ep_int);
> +
> +       /* Do some basic sanity checks, and bail if we find a problem */
> +       if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
> +           !ss->ep_in || !ss->ep_out || !ss->ep_int) {
> +               debug("Problems with device\n");
> +               return 0;

Seems this should return an error.

> +       }
> +       dev->privptr = (void *)ss;
> +
> +#ifndef CONFIG_DM_ETH
> +       /* alloc driver private */
> +       ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
> +       if (!ss->dev_priv)
> +               return 0;
> +#endif
> +
> +       return 1;
> +}
> +
> +#ifndef CONFIG_DM_ETH
> +/*
> + * lan7x callbacks
> + */
> +int lan7x_send(struct eth_device *eth, void *packet, int length)
> +{
> +       struct ueth_data *dev = (struct ueth_data *)eth->priv;
> +
> +       return lan7x_send_common(dev, packet, length);
> +}
> +
> +int lan7x_recv(struct eth_device *eth)
> +{
> +       struct ueth_data *dev = (struct ueth_data *)eth->priv;
> +
> +       return lan7x_recv_common(dev);
> +}
> +
> +void lan7x_halt(struct eth_device *eth)
> +{
> +       debug("** %s()\n", __func__);
> +}
> +#endif /* !CONFIG_DM_ETH */
> +
> +#ifdef CONFIG_DM_ETH
> +void lan7x_eth_stop(struct udevice *dev)
> +{
> +       debug("** %s()\n", __func__);
> +}
> +
> +int lan7x_eth_send(struct udevice *dev, void *packet, int length)
> +{
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +
> +       return lan7x_send_common(&priv->ueth, packet, length);
> +}
> +
> +int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp)
> +{
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +
> +       return lan7x_recv_common(&priv->ueth);
> +}
> +
> +int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len)
> +{
> +       struct lan7x_private *priv = dev_get_priv(dev);
> +
> +       packet_len = ALIGN(packet_len, 4);
> +       usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
> +
> +       return 0;
> +}
> +#endif /* CONFIG_DM_ETH */
> +
> diff --git a/drivers/usb/eth/lan7x.h b/drivers/usb/eth/lan7x.h
> new file mode 100644
> index 0000000..b5c1b39
> --- /dev/null
> +++ b/drivers/usb/eth/lan7x.h
> @@ -0,0 +1,189 @@
> +/*
> + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved.
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +
> +/* USB Vendor Requests */
> +#define USB_VENDOR_REQUEST_WRITE_REGISTER      0xA0
> +#define USB_VENDOR_REQUEST_READ_REGISTER       0xA1
> +#define USB_VENDOR_REQUEST_GET_STATS           0xA2
> +
> +/* Tx Command A */
> +#define TX_CMD_A_FCS                   BIT(22)
> +#define TX_CMD_A_LEN_MASK              0x000FFFFF
> +
> +/* Rx Command A */
> +#define RX_CMD_A_RXE                   BIT(18)
> +#define RX_CMD_A_LEN_MASK              0x00003FFF
> +
> +/* SCSRs */
> +#define ID_REV                         0x00
> +#define ID_REV_CHIP_ID_MASK            0xFFFF0000
> +#define ID_REV_CHIP_ID_7500            0x7500
> +#define ID_REV_CHIP_ID_7800            0x7800
> +#define ID_REV_CHIP_ID_7850            0x7850
> +
> +#define INT_STS                                0x0C
> +
> +#define HW_CFG                         0x010
> +#define HW_CFG_LRST                    BIT(1)
> +
> +#define PMT_CTL                                0x014
> +#define PMT_CTL_PHY_PWRUP              BIT(10)
> +#define PMT_CTL_READY                  BIT(7)
> +#define PMT_CTL_PHY_RST                        BIT(4)
> +
> +#define E2P_CMD                                0x040
> +#define E2P_CMD_EPC_BUSY               BIT(31)
> +#define E2P_CMD_EPC_CMD_READ           0x00000000
> +#define E2P_CMD_EPC_TIMEOUT            BIT(10)
> +#define E2P_CMD_EPC_ADDR_MASK          0x000001FF
> +
> +#define E2P_DATA                       0x044
> +
> +#define RFE_CTL_BCAST_EN               BIT(10)
> +#define RFE_CTL_DA_PERFECT             BIT(1)
> +
> +#define FCT_RX_CTL_EN                  BIT(31)
> +
> +#define FCT_TX_CTL_EN                  BIT(31)
> +
> +#define MAC_CR                         0x100
> +#define MAC_CR_ADP                     BIT(13)
> +#define MAC_CR_AUTO_DUPLEX             BIT(12)
> +#define MAC_CR_AUTO_SPEED              BIT(11)
> +
> +#define MAC_RX                         0x104
> +#define MAC_RX_FCS_STRIP               BIT(4)
> +#define MAC_RX_RXEN                    BIT(0)
> +
> +#define MAC_TX                         0x108
> +#define MAC_TX_TXEN                    BIT(0)
> +
> +#define FLOW                           0x10C
> +#define FLOW_CR_TX_FCEN                        BIT(30)
> +#define FLOW_CR_RX_FCEN                        BIT(29)
> +
> +#define RX_ADDRH                       0x118
> +#define RX_ADDRL                       0x11C
> +
> +#define MII_ACC                                0x120
> +#define MII_ACC_MII_READ               0x00000000
> +#define MII_ACC_MII_WRITE              0x00000002
> +#define MII_ACC_MII_BUSY               BIT(0)
> +
> +#define MII_DATA                       0x124
> +
> +#define SS_USB_PKT_SIZE                        1024
> +#define HS_USB_PKT_SIZE                        512
> +#define FS_USB_PKT_SIZE                        64
> +
> +#define MAX_RX_FIFO_SIZE               (12 * 1024)
> +#define MAX_TX_FIFO_SIZE               (12 * 1024)
> +#define DEFAULT_BULK_IN_DELAY          0x0800
> +
> +#define EEPROM_INDICATOR               0xA5
> +#define EEPROM_MAC_OFFSET              0x01
> +
> +/* Some extra defines */
> +#define LAN7X_INTERNAL_PHY_ID          1
> +
> +#define LAN7X_MAC_RX_MAX_SIZE(mtu) \
> +       ((mtu) << 16)                   /**< Max frame size */
> +#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \
> +       LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */)
> +
> +/* Timeouts */
> +#define USB_CTRL_SET_TIMEOUT_MS                5000
> +#define USB_CTRL_GET_TIMEOUT_MS                5000
> +#define USB_BULK_SEND_TIMEOUT_MS       5000
> +#define USB_BULK_RECV_TIMEOUT_MS       5000
> +#define TIMEOUT_RESOLUTION_MS          50
> +#define PHY_CONNECT_TIMEOUT_MS         5000
> +
> +#define RX_URB_SIZE    2048
> +
> +/* driver private */
> +struct lan7x_private {
> +#ifdef CONFIG_DM_ETH
> +       struct ueth_data ueth;
> +#endif
> +       int have_hwaddr;        /* 1 if we have a hardware MAC address */
> +       u32 chipid;             /* Chip or device ID */
> +};
> +
> +#ifndef CONFIG_DM_ETH
> +struct lan7x_dongle {
> +       unsigned short vendor;
> +       unsigned short product;
> +};
> +#endif
> +
> +/*
> + * Lan7x infrastructure commands
> + */
> +int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data);
> +
> +int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data);
> +
> +int lan7x_wait_for_bit(struct usb_device *udev,
> +                      const char *prefix, const u32 index,
> +                      const u32 mask, const bool set,
> +                      const unsigned int timeout_ms);
> +
> +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx);
> +
> +int lan7x_mdio_wait_for_bit(struct usb_device *udev,
> +                           const char *prefix,
> +                           int phy_id, const u32 index,
> +                           const u32 mask, const bool set,
> +                           const unsigned int timeout_ms);
> +
> +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx,
> +                     int regval);
> +
> +int lan7x_pmt_phy_reset(struct usb_device *udev,
> +                       struct ueth_data *dev);
> +
> +int lan7x_phy_initialize(struct usb_device *udev,
> +                        struct ueth_data *dev);
> +
> +int lan7x_update_flowcontrol(struct usb_device *udev,
> +                            struct ueth_data *dev,
> +                            uint32_t *flow, uint32_t *fct_flow);
> +
> +int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev);
> +
> +int lan7x_basic_reset(struct usb_device *udev,
> +                     struct ueth_data *dev);
> +
> +int lan7x_send_common(struct ueth_data *dev, void *packet, int length);
> +
> +int lan7x_recv_common(struct ueth_data *dev);
> +
> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +                   struct ueth_data *ss);
> +
> +#ifndef CONFIG_DM_ETH
> +/*
> + * lan7x callbacks
> + */
> +int lan7x_send(struct eth_device *eth, void *packet, int length);
> +
> +int lan7x_recv(struct eth_device *eth);
> +
> +void lan7x_halt(struct eth_device *eth);
> +#endif /* !CONFIG_DM_ETH */
> +
> +#ifdef CONFIG_DM_ETH
> +void lan7x_eth_stop(struct udevice *dev);
> +
> +int lan7x_eth_send(struct udevice *dev, void *packet, int length);
> +
> +int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp);
> +
> +int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len);
> +#endif /* CONFIG_DM_ETH */
> +
> diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
> index 36734e2..8f4b5e9 100644
> --- a/drivers/usb/eth/usb_ether.c
> +++ b/drivers/usb/eth/usb_ether.c
> @@ -180,6 +180,20 @@ static const struct usb_eth_prob_dev prob_dev[] = {
>                 .get_info = smsc95xx_eth_get_info,
>         },
>  #endif
> +#ifdef CONFIG_USB_ETHER_LAN75XX
> +       {
> +               .before_probe = lan75xx_eth_before_probe,
> +               .probe = lan75xx_eth_probe,
> +               .get_info = lan75xx_eth_get_info,
> +       },
> +#endif
> +#ifdef CONFIG_USB_ETHER_LAN78XX
> +       {
> +               .before_probe = lan78xx_eth_before_probe,
> +               .probe = lan78xx_eth_probe,
> +               .get_info = lan78xx_eth_get_info,
> +       },
> +#endif
>  #ifdef CONFIG_USB_ETHER_RTL8152
>         {
>                 .before_probe = r8152_eth_before_probe,
> diff --git a/include/usb_ether.h b/include/usb_ether.h
> index 51fce4e..1990b0d 100644
> --- a/include/usb_ether.h
> +++ b/include/usb_ether.h
> @@ -132,6 +132,18 @@ int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
>  int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
>                         struct eth_device *eth);
>
> +void lan75xx_eth_before_probe(void);
> +int lan75xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +                     struct ueth_data *ss);
> +int lan75xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
> +                        struct eth_device *eth);
> +
> +void lan78xx_eth_before_probe(void);
> +int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
> +                     struct ueth_data *ss);
> +int lan78xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
> +                        struct eth_device *eth);
> +
>  void r8152_eth_before_probe(void);
>  int r8152_eth_probe(struct usb_device *dev, unsigned int ifnum,
>                     struct ueth_data *ss);
> --
> 2.7.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-05-05 20:58 ` Joe Hershberger
@ 2017-05-10 15:25   ` Yuiko.Oshino at microchip.com
  2017-05-24 15:14     ` Yuiko.Oshino at microchip.com
  2017-05-25 16:43     ` Joe Hershberger
  0 siblings, 2 replies; 11+ messages in thread
From: Yuiko.Oshino at microchip.com @ 2017-05-10 15:25 UTC (permalink / raw)
  To: u-boot

From: Yuiko Oshino <yuiko.oshino@microchip.com>
>-----Original Message-----
>From: Joe Hershberger [mailto:joe.hershberger at gmail.com]
>Sent: Friday, May 5, 2017 4:59 PM
>To: Yuiko Oshino - C18177
>Cc: u-boot; Marek Vasut
>Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and
>LAN78xx
>
>Hi Yuiko,

Hi Joe!

[...]

>> +static int lan75xx_phy_gig_workaround(struct usb_device *udev,
>> +                                     struct ueth_data *dev)
>> +{
>> +       int ret = 0;
>> +
>> +       /* Only internal phy */
>> +       /* Set the phy in Gig loopback */
>> +       lan7x_mdio_write(udev, dev->phy_id, MII_BMCR,
>> +                        (BMCR_LOOPBACK | BMCR_SPEED1000));
>> +
>> +       /* Wait for the link up */
>> +       ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
>> +                                     dev->phy_id, MII_BMSR, BMSR_LSTATUS,
>> +                                     true, PHY_CONNECT_TIMEOUT_MS);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* phy reset */
>> +       ret = lan7x_pmt_phy_reset(udev, dev);
>> +       return ret;
>
>Just return lan7x_pmt_phy_reset(udev, dev);

Sure thing.

>
>> +}
>> +
>> +static int lan75xx_update_flowcontrol(struct usb_device *udev,
>> +                                     struct ueth_data *dev)
>> +{
>> +       uint32_t flow = 0, fct_flow = 0;
>> +       int ret;
>> +
>> +       ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = lan7x_write_reg(udev, FLOW, flow);
>
>       if (ret)
>              return ret;
>
>> +       ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow);
>> +       return ret;
>
>Return directly

OK.

[...]

>> +
>> +static int lan75xx_set_multicast(struct usb_device *udev)
>> +{
>> +       int ret;
>> +       u32 write_buf;
>> +
>> +       /* No multicast in u-boot */
>
>May want to... will enable IPv6 later.

Yes, later.

>
>> +       write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
>> +       ret = lan7x_write_reg(udev, LAN75XX_RFE_CTL, write_buf);
>> +
>> +       return ret;
>> +}
>> +
>> +/* starts the TX path */
>> +static void lan75xx_start_tx_path(struct usb_device *udev)
>> +{
>> +       u32 reg_val;
>> +
>> +       /* Enable Tx at MAC */
>> +       reg_val = MAC_TX_TXEN;
>
>Why not just pass it into the function directly? Applies globally when
>the assignment is a single mask.

True. I will take care of them.

>
>> +       lan7x_write_reg(udev, MAC_TX, reg_val);
>> +
>> +       /* Enable Tx at SCSRs */
>> +       reg_val = FCT_TX_CTL_EN;
>> +       lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, reg_val);
>> +}
>> +

[...]

>> +static int lan75xx_eth_probe(struct udevice *dev)
>> +{
>> +       struct usb_device *udev = dev_get_parent_priv(dev);
>> +       struct lan7x_private *priv = dev_get_priv(dev);
>> +       struct ueth_data *ueth = &priv->ueth;
>> +       struct eth_pdata *pdata = dev_get_platdata(dev);
>> +
>> +       /* Do a reset in order to get the MAC address from HW */
>> +       if (lan75xx_basic_reset(udev, ueth, priv))
>> +               return 0;
>> +
>> +       /* Get the MAC address */
>> +       /*
>> +        * We must set the eth->enetaddr from HW because the upper layer
>> +        * will force to use the environmental var (usbethaddr) or random if
>> +        * there is no valid MAC address in eth->enetaddr.
>> +        */
>> +       lan75xx_read_mac(pdata->enetaddr, udev);
>> +       /* Do not return 0 for not finding MAC addr in HW */
>> +
>> +       return usb_ether_register(dev, ueth, RX_URB_SIZE);
>> +}
>
>I agree that these can all be squashed to remove non-DM support and
>move all of the common functions up into these DM functions.
>
I will try to clean them.

[...]

>> +/*
>> + * Lan78xx infrastructure commands
>> + */
>> +static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset,
>> +                               u32 length, u8 *data)
>> +{
>> +       int i;
>> +       int ret;
>> +       u32 buf;
>> +
>> +       ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) {
>> +               /* clear it and wait to be cleared */
>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0);
>
>Either you don't care about the ret value, in which case why is there
>one, or you are losing it by overwriting it on the next call. You
>should probably be checking it after every assignment. Applies
>globally.
>

True also. I will take care of them.

>> +               ret = lan7x_wait_for_bit(udev,
>"LAN78XX_OTP_PWR_DN_PWRDN_N",
>> +                                        LAN78XX_OTP_PWR_DN,
>> +                                        LAN78XX_OTP_PWR_DN_PWRDN_N,
>> +                                        false, 1000);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       for (i = 0; i < length; i++) {
>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1,
>> +                                     ((offset + i) >> 8) &
>> +                                     LAN78XX_OTP_ADDR1_15_11);
>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2,
>> +                                     ((offset + i) & LAN78XX_OTP_ADDR2_10_3));
>> +
>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD,
>> +                                     LAN78XX_OTP_FUNC_CMD_READ);
>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO,
>> +                                     LAN78XX_OTP_CMD_GO_GO);
>> +
>> +               if (ret)
>> +                       return ret;
>> +
>> +               ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY",
>> +                                        LAN78XX_OTP_STATUS,
>> +                                        LAN78XX_OTP_STATUS_BUSY,
>> +                                        false, 1000);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf);
>> +
>> +               data[i] = (u8)(buf & 0xFF);
>> +       }
>> +
>> +       return 0;
>> +}
>> +

[...]

>> +
>> +/* Loop until the read is completed with timeout */
>> +int lan7x_wait_for_bit(struct usb_device *udev,
>> +                      const char *prefix, const u32 index,
>> +                      const u32 mask, const bool set,
>> +                      unsigned int timeout_ms)
>
>Can you not use the generic one? include/wait_bit.h

We need to use our own register read function as our device is an USB device.
It looks like wait_bit.h does not support function pointer to register read, therefore we need our own.

>
>> +{
>> +       u32 val;
>> +
>> +       while (--timeout_ms) {
>> +               lan7x_read_reg(udev, index, &val);
>> +
>> +               if (!set)
>> +                       val = ~val;
>> +
>> +               if ((val & mask) == mask)
>> +                       return 0;
>> +
>> +               mdelay(1);
>> +       }
>> +
>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>> +             prefix, index, mask, set);
>> +
>> +       return -ETIMEDOUT;
>> +}
>> +
>> +static int lan7x_phy_wait_not_busy(struct usb_device *udev)
>> +{
>> +       return lan7x_wait_for_bit(udev, __func__,
>> +                                 MII_ACC, MII_ACC_MII_BUSY,
>> +                                 false, 100);
>> +}
>> +
>> +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx)
>> +{
>> +       u32 val, addr;
>> +
>> +       /* confirm MII not busy */
>> +       if (lan7x_phy_wait_not_busy(udev)) {
>> +               debug("MII is busy in %s\n", __func__);
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       /* set the address, index & direction (read from PHY) */
>> +       addr = (phy_id << 11) | (idx << 6) |
>> +               MII_ACC_MII_READ | MII_ACC_MII_BUSY;
>> +       lan7x_write_reg(udev, MII_ACC, addr);
>> +
>> +       if (lan7x_phy_wait_not_busy(udev)) {
>> +               debug("Timed out reading MII reg %02X\n", idx);
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       lan7x_read_reg(udev, MII_DATA, &val);
>> +
>> +       return (u16) (val & 0xFFFF);
>> +}
>> +
>> +int lan7x_mdio_wait_for_bit(struct usb_device *udev,
>> +                           const char *prefix,
>> +                           int phy_id, const u32 index,
>> +                           const u32 mask, const bool set,
>> +                           unsigned int timeout_ms)
>> +{
>> +       u32 val;
>> +
>> +       while (--timeout_ms) {
>> +               val = lan7x_mdio_read(udev, phy_id, index);
>> +
>> +               if (!set)
>> +                       val = ~val;
>> +
>> +               if ((val & mask) == mask)
>> +                       return 0;
>> +
>> +               mdelay(1);
>> +       }
>> +
>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>> +             prefix, index, mask, set);
>> +
>> +       return -ETIMEDOUT;
>> +}
>> +

[...]

>> +static int lan7x_mii_get_an(uint32_t advertising_reg)
>> +{
>> +       int advertising = 0;
>> +
>> +       if (advertising_reg & LPA_LPACK)
>> +               advertising |= ADVERTISED_Autoneg;
>> +       if (advertising_reg & ADVERTISE_10HALF)
>> +               advertising |= ADVERTISED_10baseT_Half;
>> +       if (advertising_reg & ADVERTISE_10FULL)
>> +               advertising |= ADVERTISED_10baseT_Full;
>> +       if (advertising_reg & ADVERTISE_100HALF)
>> +               advertising |= ADVERTISED_100baseT_Half;
>> +       if (advertising_reg & ADVERTISE_100FULL)
>> +               advertising |= ADVERTISED_100baseT_Full;
>> +
>> +       return advertising;
>> +}
>> +
>> +int lan7x_update_flowcontrol(struct usb_device *udev,
>> +                            struct ueth_data *dev,
>> +                            uint32_t *flow, uint32_t *fct_flow)
>> +{
>> +       uint32_t lcladv, rmtadv, ctrl1000, stat1000;
>> +       uint32_t advertising = 0, lp_advertising = 0, nego = 0;
>> +       uint32_t duplex = 0;
>> +       u8 cap = 0;
>> +
>> +       lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
>> +       advertising = lan7x_mii_get_an(lcladv);
>> +
>> +       rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
>> +       lp_advertising = lan7x_mii_get_an(rmtadv);
>> +
>> +       ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
>> +       stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
>> +
>> +       if (ctrl1000 & ADVERTISE_1000HALF)
>> +               advertising |= ADVERTISED_1000baseT_Half;
>> +
>> +       if (ctrl1000 & ADVERTISE_1000FULL)
>> +               advertising |= ADVERTISED_1000baseT_Full;
>> +
>> +       if (stat1000 & LPA_1000HALF)
>> +               lp_advertising |= ADVERTISED_1000baseT_Half;
>> +
>> +       if (stat1000 & LPA_1000FULL)
>> +               lp_advertising |= ADVERTISED_1000baseT_Full;
>> +
>> +       nego = advertising & lp_advertising;
>> +
>> +       debug("LAN7x linked at ");
>> +
>> +       if (nego & (ADVERTISED_1000baseT_Full |
>ADVERTISED_1000baseT_Half)) {
>> +               debug("1000 ");
>> +               duplex = !!(nego & ADVERTISED_1000baseT_Full);
>> +
>> +       } else if (nego & (ADVERTISED_100baseT_Full |
>> +                  ADVERTISED_100baseT_Half)) {
>> +               debug("100 ");
>> +               duplex = !!(nego & ADVERTISED_100baseT_Full);
>> +       } else {
>> +               debug("10 ");
>> +               duplex = !!(nego & ADVERTISED_10baseT_Full);
>> +       }
>> +
>> +       if (duplex == DUPLEX_FULL)
>> +               debug("full dup ");
>> +       else
>> +               debug("half dup ");
>> +
>> +       if (duplex == DUPLEX_FULL) {
>> +               if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
>> +                       cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
>> +               } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
>> +                       if (lcladv & ADVERTISE_PAUSE_CAP)
>> +                               cap = FLOW_CTRL_RX;
>> +                       else if (rmtadv & LPA_PAUSE_CAP)
>> +                               cap = FLOW_CTRL_TX;
>> +               }
>> +               debug("TX Flow ");
>> +               if (cap & FLOW_CTRL_TX) {
>> +                       *flow = (FLOW_CR_TX_FCEN | 0xFFFF);
>> +                       /* set fct_flow thresholds to 20% and 80% */
>> +                       *fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
>> +                                       & 0x7FUL);
>> +                       *fct_flow <<= 8UL;
>> +                       *fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
>> +                                       & 0x7FUL);
>> +                       debug("EN ");
>> +               } else {
>> +                       debug("DIS ");
>> +               }
>> +               debug("RX Flow ");
>> +               if (cap & FLOW_CTRL_RX) {
>> +                       *flow |= FLOW_CR_RX_FCEN;
>> +                       debug("EN");
>> +               } else {
>> +                       debug("DIS");
>> +               }
>> +       }
>> +       debug("\n");
>> +       return 0;
>> +}
>
>I see where Marek is coming from wrt thisall being in phylib already.
>I guess you always have a fixed phy internal, so there's no need of
>the flexibility of phylib. Maybe there's at least opportunity to
>consolidate subroutines even if not using phylib the normal way.

I am a bit confused what you say. Do you mean that I can keep the current code as is in this area?
Please confirm?
The access to PHY also needs our own register read/write through USB, so using the phylib is more complicated.

>
>> +
>> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
>> +                   struct ueth_data *ss)
>> +{
>> +       struct usb_interface *iface;
>> +       struct usb_interface_descriptor *iface_desc;
>> +       int i;
>> +
>> +       iface = &dev->config.if_desc[ifnum];
>> +       iface_desc = &dev->config.if_desc[ifnum].desc;
>> +
>> +       memset(ss, '\0', sizeof(struct ueth_data));
>> +
>> +       /* Initialize the ueth_data structure with some useful info */
>> +       ss->ifnum = ifnum;
>> +       ss->pusb_dev = dev;
>> +       ss->subclass = iface_desc->bInterfaceSubClass;
>> +       ss->protocol = iface_desc->bInterfaceProtocol;
>> +
>> +       /*
>> +        * We are expecting a minimum of 3 endpoints
>> +        * - in, out (bulk), and int.
>> +        * We will ignore any others.
>> +        */
>> +       for (i = 0; i < iface_desc->bNumEndpoints; i++) {
>> +               /* is it an BULK endpoint? */
>> +               if ((iface->ep_desc[i].bmAttributes &
>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>USB_ENDPOINT_XFER_BULK) {
>> +                       if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
>> +                               ss->ep_in =
>> +                                       iface->ep_desc[i].bEndpointAddress &
>> +                                       USB_ENDPOINT_NUMBER_MASK;
>> +                       else
>> +                               ss->ep_out =
>> +                                       iface->ep_desc[i].bEndpointAddress &
>> +                                       USB_ENDPOINT_NUMBER_MASK;
>> +               }
>> +
>> +               /* is it an interrupt endpoint? */
>> +               if ((iface->ep_desc[i].bmAttributes &
>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>USB_ENDPOINT_XFER_INT) {
>> +                       ss->ep_int = iface->ep_desc[i].bEndpointAddress &
>> +                               USB_ENDPOINT_NUMBER_MASK;
>> +                       ss->irqinterval = iface->ep_desc[i].bInterval;
>> +               }
>> +       }
>> +       debug("Endpoints In %d Out %d Int %d\n",
>> +             ss->ep_in, ss->ep_out, ss->ep_int);
>> +
>> +       /* Do some basic sanity checks, and bail if we find a problem */
>> +       if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
>> +           !ss->ep_in || !ss->ep_out || !ss->ep_int) {
>> +               debug("Problems with device\n");
>> +               return 0;
>
>Seems this should return an error.

The caller probe_valid_drivers() in usb_ether.c expects 0 for skipping a bad device.

>
>> +       }
>> +       dev->privptr = (void *)ss;
>> +
>> +#ifndef CONFIG_DM_ETH
>> +       /* alloc driver private */
>> +       ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
>> +       if (!ss->dev_priv)
>> +               return 0;
>> +#endif
>> +
>> +       return 1;
>> +}
>> +

Thank you.
Yuiko

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-05-10 15:25   ` Yuiko.Oshino at microchip.com
@ 2017-05-24 15:14     ` Yuiko.Oshino at microchip.com
  2017-05-30 16:54       ` Joe Hershberger
  2017-05-25 16:43     ` Joe Hershberger
  1 sibling, 1 reply; 11+ messages in thread
From: Yuiko.Oshino at microchip.com @ 2017-05-24 15:14 UTC (permalink / raw)
  To: u-boot

From: Yuiko Oshino <yuiko.oshino@microchip.com>

>-----Original Message-----
>From: U-Boot [mailto:u-boot-bounces at lists.denx.de] On Behalf Of
>Yuiko.Oshino at microchip.com
>Sent: Wednesday, May 10, 2017 11:25 AM
>To: joe.hershberger at gmail.com
>Cc: marex at denx.de; u-boot at lists.denx.de
>Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and
>LAN78xx
>
>From: Yuiko Oshino <yuiko.oshino@microchip.com>
>>-----Original Message-----
>>From: Joe Hershberger [mailto:joe.hershberger at gmail.com]
>>Sent: Friday, May 5, 2017 4:59 PM
>>To: Yuiko Oshino - C18177
>>Cc: u-boot; Marek Vasut
>>Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and
>>LAN78xx
>>
>>Hi Yuiko,
>
>Hi Joe!
>
>[...]
>
>>> +static int lan75xx_phy_gig_workaround(struct usb_device *udev,
>>> +                                     struct ueth_data *dev) {
>>> +       int ret = 0;
>>> +
>>> +       /* Only internal phy */
>>> +       /* Set the phy in Gig loopback */
>>> +       lan7x_mdio_write(udev, dev->phy_id, MII_BMCR,
>>> +                        (BMCR_LOOPBACK | BMCR_SPEED1000));
>>> +
>>> +       /* Wait for the link up */
>>> +       ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
>>> +                                     dev->phy_id, MII_BMSR, BMSR_LSTATUS,
>>> +                                     true, PHY_CONNECT_TIMEOUT_MS);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       /* phy reset */
>>> +       ret = lan7x_pmt_phy_reset(udev, dev);
>>> +       return ret;
>>
>>Just return lan7x_pmt_phy_reset(udev, dev);
>
>Sure thing.
>
>>
>>> +}
>>> +
>>> +static int lan75xx_update_flowcontrol(struct usb_device *udev,
>>> +                                     struct ueth_data *dev) {
>>> +       uint32_t flow = 0, fct_flow = 0;
>>> +       int ret;
>>> +
>>> +       ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = lan7x_write_reg(udev, FLOW, flow);
>>
>>       if (ret)
>>              return ret;
>>
>>> +       ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow);
>>> +       return ret;
>>
>>Return directly
>
>OK.
>
>[...]
>
>>> +
>>> +static int lan75xx_set_multicast(struct usb_device *udev) {
>>> +       int ret;
>>> +       u32 write_buf;
>>> +
>>> +       /* No multicast in u-boot */
>>
>>May want to... will enable IPv6 later.
>
>Yes, later.
>
>>
>>> +       write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
>>> +       ret = lan7x_write_reg(udev, LAN75XX_RFE_CTL, write_buf);
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +/* starts the TX path */
>>> +static void lan75xx_start_tx_path(struct usb_device *udev) {
>>> +       u32 reg_val;
>>> +
>>> +       /* Enable Tx at MAC */
>>> +       reg_val = MAC_TX_TXEN;
>>
>>Why not just pass it into the function directly? Applies globally when
>>the assignment is a single mask.
>
>True. I will take care of them.
>
>>
>>> +       lan7x_write_reg(udev, MAC_TX, reg_val);
>>> +
>>> +       /* Enable Tx at SCSRs */
>>> +       reg_val = FCT_TX_CTL_EN;
>>> +       lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, reg_val); }
>>> +
>
>[...]
>
>>> +static int lan75xx_eth_probe(struct udevice *dev) {
>>> +       struct usb_device *udev = dev_get_parent_priv(dev);
>>> +       struct lan7x_private *priv = dev_get_priv(dev);
>>> +       struct ueth_data *ueth = &priv->ueth;
>>> +       struct eth_pdata *pdata = dev_get_platdata(dev);
>>> +
>>> +       /* Do a reset in order to get the MAC address from HW */
>>> +       if (lan75xx_basic_reset(udev, ueth, priv))
>>> +               return 0;
>>> +
>>> +       /* Get the MAC address */
>>> +       /*
>>> +        * We must set the eth->enetaddr from HW because the upper layer
>>> +        * will force to use the environmental var (usbethaddr) or random if
>>> +        * there is no valid MAC address in eth->enetaddr.
>>> +        */
>>> +       lan75xx_read_mac(pdata->enetaddr, udev);
>>> +       /* Do not return 0 for not finding MAC addr in HW */
>>> +
>>> +       return usb_ether_register(dev, ueth, RX_URB_SIZE); }
>>
>>I agree that these can all be squashed to remove non-DM support and
>>move all of the common functions up into these DM functions.
>>
>I will try to clean them.
>
>[...]
>
>>> +/*
>>> + * Lan78xx infrastructure commands
>>> + */
>>> +static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset,
>>> +                               u32 length, u8 *data) {
>>> +       int i;
>>> +       int ret;
>>> +       u32 buf;
>>> +
>>> +       ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) {
>>> +               /* clear it and wait to be cleared */
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0);
>>
>>Either you don't care about the ret value, in which case why is there
>>one, or you are losing it by overwriting it on the next call. You
>>should probably be checking it after every assignment. Applies
>>globally.
>>
>
>True also. I will take care of them.
>
>>> +               ret = lan7x_wait_for_bit(udev,
>>"LAN78XX_OTP_PWR_DN_PWRDN_N",
>>> +                                        LAN78XX_OTP_PWR_DN,
>>> +                                        LAN78XX_OTP_PWR_DN_PWRDN_N,
>>> +                                        false, 1000);
>>> +               if (ret)
>>> +                       return ret;
>>> +       }
>>> +
>>> +       for (i = 0; i < length; i++) {
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1,
>>> +                                     ((offset + i) >> 8) &
>>> +                                     LAN78XX_OTP_ADDR1_15_11);
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2,
>>> +                                     ((offset + i) &
>>> + LAN78XX_OTP_ADDR2_10_3));
>>> +
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD,
>>> +                                     LAN78XX_OTP_FUNC_CMD_READ);
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO,
>>> +                                     LAN78XX_OTP_CMD_GO_GO);
>>> +
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY",
>>> +                                        LAN78XX_OTP_STATUS,
>>> +                                        LAN78XX_OTP_STATUS_BUSY,
>>> +                                        false, 1000);
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA,
>>> + &buf);
>>> +
>>> +               data[i] = (u8)(buf & 0xFF);
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>
>[...]
>
>>> +
>>> +/* Loop until the read is completed with timeout */ int
>>> +lan7x_wait_for_bit(struct usb_device *udev,
>>> +                      const char *prefix, const u32 index,
>>> +                      const u32 mask, const bool set,
>>> +                      unsigned int timeout_ms)
>>
>>Can you not use the generic one? include/wait_bit.h
>
>We need to use our own register read function as our device is an USB device.
>It looks like wait_bit.h does not support function pointer to register read,
>therefore we need our own.
>
>>
>>> +{
>>> +       u32 val;
>>> +
>>> +       while (--timeout_ms) {
>>> +               lan7x_read_reg(udev, index, &val);
>>> +
>>> +               if (!set)
>>> +                       val = ~val;
>>> +
>>> +               if ((val & mask) == mask)
>>> +                       return 0;
>>> +
>>> +               mdelay(1);
>>> +       }
>>> +
>>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>>> +             prefix, index, mask, set);
>>> +
>>> +       return -ETIMEDOUT;
>>> +}
>>> +
>>> +static int lan7x_phy_wait_not_busy(struct usb_device *udev) {
>>> +       return lan7x_wait_for_bit(udev, __func__,
>>> +                                 MII_ACC, MII_ACC_MII_BUSY,
>>> +                                 false, 100); }
>>> +
>>> +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx) {
>>> +       u32 val, addr;
>>> +
>>> +       /* confirm MII not busy */
>>> +       if (lan7x_phy_wait_not_busy(udev)) {
>>> +               debug("MII is busy in %s\n", __func__);
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       /* set the address, index & direction (read from PHY) */
>>> +       addr = (phy_id << 11) | (idx << 6) |
>>> +               MII_ACC_MII_READ | MII_ACC_MII_BUSY;
>>> +       lan7x_write_reg(udev, MII_ACC, addr);
>>> +
>>> +       if (lan7x_phy_wait_not_busy(udev)) {
>>> +               debug("Timed out reading MII reg %02X\n", idx);
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       lan7x_read_reg(udev, MII_DATA, &val);
>>> +
>>> +       return (u16) (val & 0xFFFF);
>>> +}
>>> +
>>> +int lan7x_mdio_wait_for_bit(struct usb_device *udev,
>>> +                           const char *prefix,
>>> +                           int phy_id, const u32 index,
>>> +                           const u32 mask, const bool set,
>>> +                           unsigned int timeout_ms) {
>>> +       u32 val;
>>> +
>>> +       while (--timeout_ms) {
>>> +               val = lan7x_mdio_read(udev, phy_id, index);
>>> +
>>> +               if (!set)
>>> +                       val = ~val;
>>> +
>>> +               if ((val & mask) == mask)
>>> +                       return 0;
>>> +
>>> +               mdelay(1);
>>> +       }
>>> +
>>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>>> +             prefix, index, mask, set);
>>> +
>>> +       return -ETIMEDOUT;
>>> +}
>>> +
>
>[...]
>
>>> +static int lan7x_mii_get_an(uint32_t advertising_reg) {
>>> +       int advertising = 0;
>>> +
>>> +       if (advertising_reg & LPA_LPACK)
>>> +               advertising |= ADVERTISED_Autoneg;
>>> +       if (advertising_reg & ADVERTISE_10HALF)
>>> +               advertising |= ADVERTISED_10baseT_Half;
>>> +       if (advertising_reg & ADVERTISE_10FULL)
>>> +               advertising |= ADVERTISED_10baseT_Full;
>>> +       if (advertising_reg & ADVERTISE_100HALF)
>>> +               advertising |= ADVERTISED_100baseT_Half;
>>> +       if (advertising_reg & ADVERTISE_100FULL)
>>> +               advertising |= ADVERTISED_100baseT_Full;
>>> +
>>> +       return advertising;
>>> +}
>>> +
>>> +int lan7x_update_flowcontrol(struct usb_device *udev,
>>> +                            struct ueth_data *dev,
>>> +                            uint32_t *flow, uint32_t *fct_flow) {
>>> +       uint32_t lcladv, rmtadv, ctrl1000, stat1000;
>>> +       uint32_t advertising = 0, lp_advertising = 0, nego = 0;
>>> +       uint32_t duplex = 0;
>>> +       u8 cap = 0;
>>> +
>>> +       lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
>>> +       advertising = lan7x_mii_get_an(lcladv);
>>> +
>>> +       rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
>>> +       lp_advertising = lan7x_mii_get_an(rmtadv);
>>> +
>>> +       ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
>>> +       stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
>>> +
>>> +       if (ctrl1000 & ADVERTISE_1000HALF)
>>> +               advertising |= ADVERTISED_1000baseT_Half;
>>> +
>>> +       if (ctrl1000 & ADVERTISE_1000FULL)
>>> +               advertising |= ADVERTISED_1000baseT_Full;
>>> +
>>> +       if (stat1000 & LPA_1000HALF)
>>> +               lp_advertising |= ADVERTISED_1000baseT_Half;
>>> +
>>> +       if (stat1000 & LPA_1000FULL)
>>> +               lp_advertising |= ADVERTISED_1000baseT_Full;
>>> +
>>> +       nego = advertising & lp_advertising;
>>> +
>>> +       debug("LAN7x linked at ");
>>> +
>>> +       if (nego & (ADVERTISED_1000baseT_Full |
>>ADVERTISED_1000baseT_Half)) {
>>> +               debug("1000 ");
>>> +               duplex = !!(nego & ADVERTISED_1000baseT_Full);
>>> +
>>> +       } else if (nego & (ADVERTISED_100baseT_Full |
>>> +                  ADVERTISED_100baseT_Half)) {
>>> +               debug("100 ");
>>> +               duplex = !!(nego & ADVERTISED_100baseT_Full);
>>> +       } else {
>>> +               debug("10 ");
>>> +               duplex = !!(nego & ADVERTISED_10baseT_Full);
>>> +       }
>>> +
>>> +       if (duplex == DUPLEX_FULL)
>>> +               debug("full dup ");
>>> +       else
>>> +               debug("half dup ");
>>> +
>>> +       if (duplex == DUPLEX_FULL) {
>>> +               if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
>>> +                       cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
>>> +               } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
>>> +                       if (lcladv & ADVERTISE_PAUSE_CAP)
>>> +                               cap = FLOW_CTRL_RX;
>>> +                       else if (rmtadv & LPA_PAUSE_CAP)
>>> +                               cap = FLOW_CTRL_TX;
>>> +               }
>>> +               debug("TX Flow ");
>>> +               if (cap & FLOW_CTRL_TX) {
>>> +                       *flow = (FLOW_CR_TX_FCEN | 0xFFFF);
>>> +                       /* set fct_flow thresholds to 20% and 80% */
>>> +                       *fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
>>> +                                       & 0x7FUL);
>>> +                       *fct_flow <<= 8UL;
>>> +                       *fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
>>> +                                       & 0x7FUL);
>>> +                       debug("EN ");
>>> +               } else {
>>> +                       debug("DIS ");
>>> +               }
>>> +               debug("RX Flow ");
>>> +               if (cap & FLOW_CTRL_RX) {
>>> +                       *flow |= FLOW_CR_RX_FCEN;
>>> +                       debug("EN");
>>> +               } else {
>>> +                       debug("DIS");
>>> +               }
>>> +       }
>>> +       debug("\n");
>>> +       return 0;
>>> +}
>>
>>I see where Marek is coming from wrt thisall being in phylib already.
>>I guess you always have a fixed phy internal, so there's no need of the
>>flexibility of phylib. Maybe there's at least opportunity to
>>consolidate subroutines even if not using phylib the normal way.
>
>I am a bit confused what you say. Do you mean that I can keep the current
>code as is in this area?
>Please confirm?
>The access to PHY also needs our own register read/write through USB, so
>using the phylib is more complicated.
>
>>
>>> +
>>> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
>>> +                   struct ueth_data *ss) {
>>> +       struct usb_interface *iface;
>>> +       struct usb_interface_descriptor *iface_desc;
>>> +       int i;
>>> +
>>> +       iface = &dev->config.if_desc[ifnum];
>>> +       iface_desc = &dev->config.if_desc[ifnum].desc;
>>> +
>>> +       memset(ss, '\0', sizeof(struct ueth_data));
>>> +
>>> +       /* Initialize the ueth_data structure with some useful info */
>>> +       ss->ifnum = ifnum;
>>> +       ss->pusb_dev = dev;
>>> +       ss->subclass = iface_desc->bInterfaceSubClass;
>>> +       ss->protocol = iface_desc->bInterfaceProtocol;
>>> +
>>> +       /*
>>> +        * We are expecting a minimum of 3 endpoints
>>> +        * - in, out (bulk), and int.
>>> +        * We will ignore any others.
>>> +        */
>>> +       for (i = 0; i < iface_desc->bNumEndpoints; i++) {
>>> +               /* is it an BULK endpoint? */
>>> +               if ((iface->ep_desc[i].bmAttributes &
>>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>>USB_ENDPOINT_XFER_BULK) {
>>> +                       if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
>>> +                               ss->ep_in =
>>> +                                       iface->ep_desc[i].bEndpointAddress &
>>> +                                       USB_ENDPOINT_NUMBER_MASK;
>>> +                       else
>>> +                               ss->ep_out =
>>> +                                       iface->ep_desc[i].bEndpointAddress &
>>> +                                       USB_ENDPOINT_NUMBER_MASK;
>>> +               }
>>> +
>>> +               /* is it an interrupt endpoint? */
>>> +               if ((iface->ep_desc[i].bmAttributes &
>>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>>USB_ENDPOINT_XFER_INT) {
>>> +                       ss->ep_int = iface->ep_desc[i].bEndpointAddress &
>>> +                               USB_ENDPOINT_NUMBER_MASK;
>>> +                       ss->irqinterval = iface->ep_desc[i].bInterval;
>>> +               }
>>> +       }
>>> +       debug("Endpoints In %d Out %d Int %d\n",
>>> +             ss->ep_in, ss->ep_out, ss->ep_int);
>>> +
>>> +       /* Do some basic sanity checks, and bail if we find a problem */
>>> +       if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
>>> +           !ss->ep_in || !ss->ep_out || !ss->ep_int) {
>>> +               debug("Problems with device\n");
>>> +               return 0;
>>
>>Seems this should return an error.
>
>The caller probe_valid_drivers() in usb_ether.c expects 0 for skipping a bad
>device.
>
>>
>>> +       }
>>> +       dev->privptr = (void *)ss;
>>> +
>>> +#ifndef CONFIG_DM_ETH
>>> +       /* alloc driver private */
>>> +       ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
>>> +       if (!ss->dev_priv)
>>> +               return 0;
>>> +#endif
>>> +
>>> +       return 1;
>>> +}
>>> +
>
>Thank you.
>Yuiko
>_______________________________________________
>U-Boot mailing list
>U-Boot at lists.denx.de
>https://lists.denx.de/listinfo/u-boot

Joe,
Please reply so that I can re-submit the patch.
Thank you in advance.
Best regards,
Yuiko

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-05-10 15:25   ` Yuiko.Oshino at microchip.com
  2017-05-24 15:14     ` Yuiko.Oshino at microchip.com
@ 2017-05-25 16:43     ` Joe Hershberger
  1 sibling, 0 replies; 11+ messages in thread
From: Joe Hershberger @ 2017-05-25 16:43 UTC (permalink / raw)
  To: u-boot

On Wed, May 10, 2017 at 10:25 AM,  <Yuiko.Oshino@microchip.com> wrote:
> From: Yuiko Oshino <yuiko.oshino@microchip.com>
>>-----Original Message-----
>>From: Joe Hershberger [mailto:joe.hershberger at gmail.com]
>>Sent: Friday, May 5, 2017 4:59 PM
>>To: Yuiko Oshino - C18177
>>Cc: u-boot; Marek Vasut
>>Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and
>>LAN78xx
>>
>>Hi Yuiko,
>
> Hi Joe!
>
> [...]
>
>>> +static int lan75xx_phy_gig_workaround(struct usb_device *udev,
>>> +                                     struct ueth_data *dev)
>>> +{
>>> +       int ret = 0;
>>> +
>>> +       /* Only internal phy */
>>> +       /* Set the phy in Gig loopback */
>>> +       lan7x_mdio_write(udev, dev->phy_id, MII_BMCR,
>>> +                        (BMCR_LOOPBACK | BMCR_SPEED1000));
>>> +
>>> +       /* Wait for the link up */
>>> +       ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS",
>>> +                                     dev->phy_id, MII_BMSR, BMSR_LSTATUS,
>>> +                                     true, PHY_CONNECT_TIMEOUT_MS);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       /* phy reset */
>>> +       ret = lan7x_pmt_phy_reset(udev, dev);
>>> +       return ret;
>>
>>Just return lan7x_pmt_phy_reset(udev, dev);
>
> Sure thing.
>
>>
>>> +}
>>> +
>>> +static int lan75xx_update_flowcontrol(struct usb_device *udev,
>>> +                                     struct ueth_data *dev)
>>> +{
>>> +       uint32_t flow = 0, fct_flow = 0;
>>> +       int ret;
>>> +
>>> +       ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = lan7x_write_reg(udev, FLOW, flow);
>>
>>       if (ret)
>>              return ret;
>>
>>> +       ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow);
>>> +       return ret;
>>
>>Return directly
>
> OK.
>
> [...]
>
>>> +
>>> +static int lan75xx_set_multicast(struct usb_device *udev)
>>> +{
>>> +       int ret;
>>> +       u32 write_buf;
>>> +
>>> +       /* No multicast in u-boot */
>>
>>May want to... will enable IPv6 later.
>
> Yes, later.
>
>>
>>> +       write_buf = RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT;
>>> +       ret = lan7x_write_reg(udev, LAN75XX_RFE_CTL, write_buf);
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +/* starts the TX path */
>>> +static void lan75xx_start_tx_path(struct usb_device *udev)
>>> +{
>>> +       u32 reg_val;
>>> +
>>> +       /* Enable Tx at MAC */
>>> +       reg_val = MAC_TX_TXEN;
>>
>>Why not just pass it into the function directly? Applies globally when
>>the assignment is a single mask.
>
> True. I will take care of them.
>
>>
>>> +       lan7x_write_reg(udev, MAC_TX, reg_val);
>>> +
>>> +       /* Enable Tx at SCSRs */
>>> +       reg_val = FCT_TX_CTL_EN;
>>> +       lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, reg_val);
>>> +}
>>> +
>
> [...]
>
>>> +static int lan75xx_eth_probe(struct udevice *dev)
>>> +{
>>> +       struct usb_device *udev = dev_get_parent_priv(dev);
>>> +       struct lan7x_private *priv = dev_get_priv(dev);
>>> +       struct ueth_data *ueth = &priv->ueth;
>>> +       struct eth_pdata *pdata = dev_get_platdata(dev);
>>> +
>>> +       /* Do a reset in order to get the MAC address from HW */
>>> +       if (lan75xx_basic_reset(udev, ueth, priv))
>>> +               return 0;
>>> +
>>> +       /* Get the MAC address */
>>> +       /*
>>> +        * We must set the eth->enetaddr from HW because the upper layer
>>> +        * will force to use the environmental var (usbethaddr) or random if
>>> +        * there is no valid MAC address in eth->enetaddr.
>>> +        */
>>> +       lan75xx_read_mac(pdata->enetaddr, udev);
>>> +       /* Do not return 0 for not finding MAC addr in HW */
>>> +
>>> +       return usb_ether_register(dev, ueth, RX_URB_SIZE);
>>> +}
>>
>>I agree that these can all be squashed to remove non-DM support and
>>move all of the common functions up into these DM functions.
>>
> I will try to clean them.
>
> [...]
>
>>> +/*
>>> + * Lan78xx infrastructure commands
>>> + */
>>> +static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset,
>>> +                               u32 length, u8 *data)
>>> +{
>>> +       int i;
>>> +       int ret;
>>> +       u32 buf;
>>> +
>>> +       ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) {
>>> +               /* clear it and wait to be cleared */
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0);
>>
>>Either you don't care about the ret value, in which case why is there
>>one, or you are losing it by overwriting it on the next call. You
>>should probably be checking it after every assignment. Applies
>>globally.
>>
>
> True also. I will take care of them.
>
>>> +               ret = lan7x_wait_for_bit(udev,
>>"LAN78XX_OTP_PWR_DN_PWRDN_N",
>>> +                                        LAN78XX_OTP_PWR_DN,
>>> +                                        LAN78XX_OTP_PWR_DN_PWRDN_N,
>>> +                                        false, 1000);
>>> +               if (ret)
>>> +                       return ret;
>>> +       }
>>> +
>>> +       for (i = 0; i < length; i++) {
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1,
>>> +                                     ((offset + i) >> 8) &
>>> +                                     LAN78XX_OTP_ADDR1_15_11);
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2,
>>> +                                     ((offset + i) & LAN78XX_OTP_ADDR2_10_3));
>>> +
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD,
>>> +                                     LAN78XX_OTP_FUNC_CMD_READ);
>>> +               ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO,
>>> +                                     LAN78XX_OTP_CMD_GO_GO);
>>> +
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY",
>>> +                                        LAN78XX_OTP_STATUS,
>>> +                                        LAN78XX_OTP_STATUS_BUSY,
>>> +                                        false, 1000);
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf);
>>> +
>>> +               data[i] = (u8)(buf & 0xFF);
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>
> [...]
>
>>> +
>>> +/* Loop until the read is completed with timeout */
>>> +int lan7x_wait_for_bit(struct usb_device *udev,
>>> +                      const char *prefix, const u32 index,
>>> +                      const u32 mask, const bool set,
>>> +                      unsigned int timeout_ms)
>>
>>Can you not use the generic one? include/wait_bit.h
>
> We need to use our own register read function as our device is an USB device.
> It looks like wait_bit.h does not support function pointer to register read, therefore we need our own.

At least copy the real one.

>>> +{
>>> +       u32 val;
>>> +
>>> +       while (--timeout_ms) {
>>> +               lan7x_read_reg(udev, index, &val);
>>> +
>>> +               if (!set)
>>> +                       val = ~val;
>>> +
>>> +               if ((val & mask) == mask)
>>> +                       return 0;
>>> +
>>> +               mdelay(1);
>>> +       }
>>> +
>>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>>> +             prefix, index, mask, set);
>>> +
>>> +       return -ETIMEDOUT;
>>> +}
>>> +
>>> +static int lan7x_phy_wait_not_busy(struct usb_device *udev)
>>> +{
>>> +       return lan7x_wait_for_bit(udev, __func__,
>>> +                                 MII_ACC, MII_ACC_MII_BUSY,
>>> +                                 false, 100);
>>> +}
>>> +
>>> +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx)
>>> +{
>>> +       u32 val, addr;
>>> +
>>> +       /* confirm MII not busy */
>>> +       if (lan7x_phy_wait_not_busy(udev)) {
>>> +               debug("MII is busy in %s\n", __func__);
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       /* set the address, index & direction (read from PHY) */
>>> +       addr = (phy_id << 11) | (idx << 6) |
>>> +               MII_ACC_MII_READ | MII_ACC_MII_BUSY;
>>> +       lan7x_write_reg(udev, MII_ACC, addr);
>>> +
>>> +       if (lan7x_phy_wait_not_busy(udev)) {
>>> +               debug("Timed out reading MII reg %02X\n", idx);
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       lan7x_read_reg(udev, MII_DATA, &val);
>>> +
>>> +       return (u16) (val & 0xFFFF);
>>> +}
>>> +
>>> +int lan7x_mdio_wait_for_bit(struct usb_device *udev,
>>> +                           const char *prefix,
>>> +                           int phy_id, const u32 index,
>>> +                           const u32 mask, const bool set,
>>> +                           unsigned int timeout_ms)
>>> +{
>>> +       u32 val;
>>> +
>>> +       while (--timeout_ms) {
>>> +               val = lan7x_mdio_read(udev, phy_id, index);
>>> +
>>> +               if (!set)
>>> +                       val = ~val;
>>> +
>>> +               if ((val & mask) == mask)
>>> +                       return 0;
>>> +
>>> +               mdelay(1);
>>> +       }
>>> +
>>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>>> +             prefix, index, mask, set);
>>> +
>>> +       return -ETIMEDOUT;
>>> +}
>>> +
>
> [...]
>
>>> +static int lan7x_mii_get_an(uint32_t advertising_reg)
>>> +{
>>> +       int advertising = 0;
>>> +
>>> +       if (advertising_reg & LPA_LPACK)
>>> +               advertising |= ADVERTISED_Autoneg;
>>> +       if (advertising_reg & ADVERTISE_10HALF)
>>> +               advertising |= ADVERTISED_10baseT_Half;
>>> +       if (advertising_reg & ADVERTISE_10FULL)
>>> +               advertising |= ADVERTISED_10baseT_Full;
>>> +       if (advertising_reg & ADVERTISE_100HALF)
>>> +               advertising |= ADVERTISED_100baseT_Half;
>>> +       if (advertising_reg & ADVERTISE_100FULL)
>>> +               advertising |= ADVERTISED_100baseT_Full;
>>> +
>>> +       return advertising;
>>> +}
>>> +
>>> +int lan7x_update_flowcontrol(struct usb_device *udev,
>>> +                            struct ueth_data *dev,
>>> +                            uint32_t *flow, uint32_t *fct_flow)
>>> +{
>>> +       uint32_t lcladv, rmtadv, ctrl1000, stat1000;
>>> +       uint32_t advertising = 0, lp_advertising = 0, nego = 0;
>>> +       uint32_t duplex = 0;
>>> +       u8 cap = 0;
>>> +
>>> +       lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
>>> +       advertising = lan7x_mii_get_an(lcladv);
>>> +
>>> +       rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
>>> +       lp_advertising = lan7x_mii_get_an(rmtadv);
>>> +
>>> +       ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
>>> +       stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
>>> +
>>> +       if (ctrl1000 & ADVERTISE_1000HALF)
>>> +               advertising |= ADVERTISED_1000baseT_Half;
>>> +
>>> +       if (ctrl1000 & ADVERTISE_1000FULL)
>>> +               advertising |= ADVERTISED_1000baseT_Full;
>>> +
>>> +       if (stat1000 & LPA_1000HALF)
>>> +               lp_advertising |= ADVERTISED_1000baseT_Half;
>>> +
>>> +       if (stat1000 & LPA_1000FULL)
>>> +               lp_advertising |= ADVERTISED_1000baseT_Full;
>>> +
>>> +       nego = advertising & lp_advertising;
>>> +
>>> +       debug("LAN7x linked at ");
>>> +
>>> +       if (nego & (ADVERTISED_1000baseT_Full |
>>ADVERTISED_1000baseT_Half)) {
>>> +               debug("1000 ");
>>> +               duplex = !!(nego & ADVERTISED_1000baseT_Full);
>>> +
>>> +       } else if (nego & (ADVERTISED_100baseT_Full |
>>> +                  ADVERTISED_100baseT_Half)) {
>>> +               debug("100 ");
>>> +               duplex = !!(nego & ADVERTISED_100baseT_Full);
>>> +       } else {
>>> +               debug("10 ");
>>> +               duplex = !!(nego & ADVERTISED_10baseT_Full);
>>> +       }
>>> +
>>> +       if (duplex == DUPLEX_FULL)
>>> +               debug("full dup ");
>>> +       else
>>> +               debug("half dup ");
>>> +
>>> +       if (duplex == DUPLEX_FULL) {
>>> +               if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
>>> +                       cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
>>> +               } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
>>> +                       if (lcladv & ADVERTISE_PAUSE_CAP)
>>> +                               cap = FLOW_CTRL_RX;
>>> +                       else if (rmtadv & LPA_PAUSE_CAP)
>>> +                               cap = FLOW_CTRL_TX;
>>> +               }
>>> +               debug("TX Flow ");
>>> +               if (cap & FLOW_CTRL_TX) {
>>> +                       *flow = (FLOW_CR_TX_FCEN | 0xFFFF);
>>> +                       /* set fct_flow thresholds to 20% and 80% */
>>> +                       *fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
>>> +                                       & 0x7FUL);
>>> +                       *fct_flow <<= 8UL;
>>> +                       *fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
>>> +                                       & 0x7FUL);
>>> +                       debug("EN ");
>>> +               } else {
>>> +                       debug("DIS ");
>>> +               }
>>> +               debug("RX Flow ");
>>> +               if (cap & FLOW_CTRL_RX) {
>>> +                       *flow |= FLOW_CR_RX_FCEN;
>>> +                       debug("EN");
>>> +               } else {
>>> +                       debug("DIS");
>>> +               }
>>> +       }
>>> +       debug("\n");
>>> +       return 0;
>>> +}
>>
>>I see where Marek is coming from wrt thisall being in phylib already.
>>I guess you always have a fixed phy internal, so there's no need of
>>the flexibility of phylib. Maybe there's at least opportunity to
>>consolidate subroutines even if not using phylib the normal way.
>
> I am a bit confused what you say. Do you mean that I can keep the current code as is in this area?
> Please confirm?
> The access to PHY also needs our own register read/write through USB, so using the phylib is more complicated.

The thought here is to do a minor refactor of the phy.c code such that
all this parsing of the bits can be shared code, while access to the
MDIO interface is specialized for your driver.

>>
>>> +
>>> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
>>> +                   struct ueth_data *ss)
>>> +{
>>> +       struct usb_interface *iface;
>>> +       struct usb_interface_descriptor *iface_desc;
>>> +       int i;
>>> +
>>> +       iface = &dev->config.if_desc[ifnum];
>>> +       iface_desc = &dev->config.if_desc[ifnum].desc;
>>> +
>>> +       memset(ss, '\0', sizeof(struct ueth_data));
>>> +
>>> +       /* Initialize the ueth_data structure with some useful info */
>>> +       ss->ifnum = ifnum;
>>> +       ss->pusb_dev = dev;
>>> +       ss->subclass = iface_desc->bInterfaceSubClass;
>>> +       ss->protocol = iface_desc->bInterfaceProtocol;
>>> +
>>> +       /*
>>> +        * We are expecting a minimum of 3 endpoints
>>> +        * - in, out (bulk), and int.
>>> +        * We will ignore any others.
>>> +        */
>>> +       for (i = 0; i < iface_desc->bNumEndpoints; i++) {
>>> +               /* is it an BULK endpoint? */
>>> +               if ((iface->ep_desc[i].bmAttributes &
>>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>>USB_ENDPOINT_XFER_BULK) {
>>> +                       if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
>>> +                               ss->ep_in =
>>> +                                       iface->ep_desc[i].bEndpointAddress &
>>> +                                       USB_ENDPOINT_NUMBER_MASK;
>>> +                       else
>>> +                               ss->ep_out =
>>> +                                       iface->ep_desc[i].bEndpointAddress &
>>> +                                       USB_ENDPOINT_NUMBER_MASK;
>>> +               }
>>> +
>>> +               /* is it an interrupt endpoint? */
>>> +               if ((iface->ep_desc[i].bmAttributes &
>>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>>USB_ENDPOINT_XFER_INT) {
>>> +                       ss->ep_int = iface->ep_desc[i].bEndpointAddress &
>>> +                               USB_ENDPOINT_NUMBER_MASK;
>>> +                       ss->irqinterval = iface->ep_desc[i].bInterval;
>>> +               }
>>> +       }
>>> +       debug("Endpoints In %d Out %d Int %d\n",
>>> +             ss->ep_in, ss->ep_out, ss->ep_int);
>>> +
>>> +       /* Do some basic sanity checks, and bail if we find a problem */
>>> +       if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
>>> +           !ss->ep_in || !ss->ep_out || !ss->ep_int) {
>>> +               debug("Problems with device\n");
>>> +               return 0;
>>
>>Seems this should return an error.
>
> The caller probe_valid_drivers() in usb_ether.c expects 0 for skipping a bad device.

OK.

>>
>>> +       }
>>> +       dev->privptr = (void *)ss;
>>> +
>>> +#ifndef CONFIG_DM_ETH
>>> +       /* alloc driver private */
>>> +       ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
>>> +       if (!ss->dev_priv)
>>> +               return 0;
>>> +#endif
>>> +
>>> +       return 1;
>>> +}
>>> +
>
> Thank you.
> Yuiko

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
  2017-05-24 15:14     ` Yuiko.Oshino at microchip.com
@ 2017-05-30 16:54       ` Joe Hershberger
  0 siblings, 0 replies; 11+ messages in thread
From: Joe Hershberger @ 2017-05-30 16:54 UTC (permalink / raw)
  To: u-boot

On Wed, May 24, 2017 at 10:14 AM,  <Yuiko.Oshino@microchip.com> wrote:
> From: Yuiko Oshino <yuiko.oshino@microchip.com>
>
>>-----Original Message-----
>>From: U-Boot [mailto:u-boot-bounces at lists.denx.de] On Behalf Of
>>Yuiko.Oshino at microchip.com
>>Sent: Wednesday, May 10, 2017 11:25 AM
>>To: joe.hershberger at gmail.com
>>Cc: marex at denx.de; u-boot at lists.denx.de
>>Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and
>>LAN78xx
>>
>>From: Yuiko Oshino <yuiko.oshino@microchip.com>
>>>-----Original Message-----
>>>From: Joe Hershberger [mailto:joe.hershberger at gmail.com]
>>>Sent: Friday, May 5, 2017 4:59 PM
>>>To: Yuiko Oshino - C18177
>>>Cc: u-boot; Marek Vasut
>>>Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and
>>>LAN78xx
>>>
>>>Hi Yuiko,
>>
>>Hi Joe!
>>
>>[...]
[...]
>>>> +
>>>> +/* Loop until the read is completed with timeout */ int
>>>> +lan7x_wait_for_bit(struct usb_device *udev,
>>>> +                      const char *prefix, const u32 index,
>>>> +                      const u32 mask, const bool set,
>>>> +                      unsigned int timeout_ms)
>>>
>>>Can you not use the generic one? include/wait_bit.h
>>
>>We need to use our own register read function as our device is an USB device.
>>It looks like wait_bit.h does not support function pointer to register read,
>>therefore we need our own.

At least copy the real one.

>>>
>>>> +{
>>>> +       u32 val;
>>>> +
>>>> +       while (--timeout_ms) {
>>>> +               lan7x_read_reg(udev, index, &val);
>>>> +
>>>> +               if (!set)
>>>> +                       val = ~val;
>>>> +
>>>> +               if ((val & mask) == mask)
>>>> +                       return 0;
>>>> +
>>>> +               mdelay(1);
>>>> +       }
>>>> +
>>>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>>>> +             prefix, index, mask, set);
>>>> +
>>>> +       return -ETIMEDOUT;
>>>> +}
>>>> +
>>>> +static int lan7x_phy_wait_not_busy(struct usb_device *udev) {
>>>> +       return lan7x_wait_for_bit(udev, __func__,
>>>> +                                 MII_ACC, MII_ACC_MII_BUSY,
>>>> +                                 false, 100); }
>>>> +
>>>> +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx) {
>>>> +       u32 val, addr;
>>>> +
>>>> +       /* confirm MII not busy */
>>>> +       if (lan7x_phy_wait_not_busy(udev)) {
>>>> +               debug("MII is busy in %s\n", __func__);
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       /* set the address, index & direction (read from PHY) */
>>>> +       addr = (phy_id << 11) | (idx << 6) |
>>>> +               MII_ACC_MII_READ | MII_ACC_MII_BUSY;
>>>> +       lan7x_write_reg(udev, MII_ACC, addr);
>>>> +
>>>> +       if (lan7x_phy_wait_not_busy(udev)) {
>>>> +               debug("Timed out reading MII reg %02X\n", idx);
>>>> +               return -ETIMEDOUT;
>>>> +       }
>>>> +
>>>> +       lan7x_read_reg(udev, MII_DATA, &val);
>>>> +
>>>> +       return (u16) (val & 0xFFFF);
>>>> +}
>>>> +
>>>> +int lan7x_mdio_wait_for_bit(struct usb_device *udev,
>>>> +                           const char *prefix,
>>>> +                           int phy_id, const u32 index,
>>>> +                           const u32 mask, const bool set,
>>>> +                           unsigned int timeout_ms) {
>>>> +       u32 val;
>>>> +
>>>> +       while (--timeout_ms) {
>>>> +               val = lan7x_mdio_read(udev, phy_id, index);
>>>> +
>>>> +               if (!set)
>>>> +                       val = ~val;
>>>> +
>>>> +               if ((val & mask) == mask)
>>>> +                       return 0;
>>>> +
>>>> +               mdelay(1);
>>>> +       }
>>>> +
>>>> +       debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
>>>> +             prefix, index, mask, set);
>>>> +
>>>> +       return -ETIMEDOUT;
>>>> +}
>>>> +
>>
>>[...]
>>
>>>> +static int lan7x_mii_get_an(uint32_t advertising_reg) {
>>>> +       int advertising = 0;
>>>> +
>>>> +       if (advertising_reg & LPA_LPACK)
>>>> +               advertising |= ADVERTISED_Autoneg;
>>>> +       if (advertising_reg & ADVERTISE_10HALF)
>>>> +               advertising |= ADVERTISED_10baseT_Half;
>>>> +       if (advertising_reg & ADVERTISE_10FULL)
>>>> +               advertising |= ADVERTISED_10baseT_Full;
>>>> +       if (advertising_reg & ADVERTISE_100HALF)
>>>> +               advertising |= ADVERTISED_100baseT_Half;
>>>> +       if (advertising_reg & ADVERTISE_100FULL)
>>>> +               advertising |= ADVERTISED_100baseT_Full;
>>>> +
>>>> +       return advertising;
>>>> +}
>>>> +
>>>> +int lan7x_update_flowcontrol(struct usb_device *udev,
>>>> +                            struct ueth_data *dev,
>>>> +                            uint32_t *flow, uint32_t *fct_flow) {
>>>> +       uint32_t lcladv, rmtadv, ctrl1000, stat1000;
>>>> +       uint32_t advertising = 0, lp_advertising = 0, nego = 0;
>>>> +       uint32_t duplex = 0;
>>>> +       u8 cap = 0;
>>>> +
>>>> +       lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
>>>> +       advertising = lan7x_mii_get_an(lcladv);
>>>> +
>>>> +       rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
>>>> +       lp_advertising = lan7x_mii_get_an(rmtadv);
>>>> +
>>>> +       ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
>>>> +       stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
>>>> +
>>>> +       if (ctrl1000 & ADVERTISE_1000HALF)
>>>> +               advertising |= ADVERTISED_1000baseT_Half;
>>>> +
>>>> +       if (ctrl1000 & ADVERTISE_1000FULL)
>>>> +               advertising |= ADVERTISED_1000baseT_Full;
>>>> +
>>>> +       if (stat1000 & LPA_1000HALF)
>>>> +               lp_advertising |= ADVERTISED_1000baseT_Half;
>>>> +
>>>> +       if (stat1000 & LPA_1000FULL)
>>>> +               lp_advertising |= ADVERTISED_1000baseT_Full;
>>>> +
>>>> +       nego = advertising & lp_advertising;
>>>> +
>>>> +       debug("LAN7x linked at ");
>>>> +
>>>> +       if (nego & (ADVERTISED_1000baseT_Full |
>>>ADVERTISED_1000baseT_Half)) {
>>>> +               debug("1000 ");
>>>> +               duplex = !!(nego & ADVERTISED_1000baseT_Full);
>>>> +
>>>> +       } else if (nego & (ADVERTISED_100baseT_Full |
>>>> +                  ADVERTISED_100baseT_Half)) {
>>>> +               debug("100 ");
>>>> +               duplex = !!(nego & ADVERTISED_100baseT_Full);
>>>> +       } else {
>>>> +               debug("10 ");
>>>> +               duplex = !!(nego & ADVERTISED_10baseT_Full);
>>>> +       }
>>>> +
>>>> +       if (duplex == DUPLEX_FULL)
>>>> +               debug("full dup ");
>>>> +       else
>>>> +               debug("half dup ");
>>>> +
>>>> +       if (duplex == DUPLEX_FULL) {
>>>> +               if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
>>>> +                       cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
>>>> +               } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
>>>> +                       if (lcladv & ADVERTISE_PAUSE_CAP)
>>>> +                               cap = FLOW_CTRL_RX;
>>>> +                       else if (rmtadv & LPA_PAUSE_CAP)
>>>> +                               cap = FLOW_CTRL_TX;
>>>> +               }
>>>> +               debug("TX Flow ");
>>>> +               if (cap & FLOW_CTRL_TX) {
>>>> +                       *flow = (FLOW_CR_TX_FCEN | 0xFFFF);
>>>> +                       /* set fct_flow thresholds to 20% and 80% */
>>>> +                       *fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
>>>> +                                       & 0x7FUL);
>>>> +                       *fct_flow <<= 8UL;
>>>> +                       *fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
>>>> +                                       & 0x7FUL);
>>>> +                       debug("EN ");
>>>> +               } else {
>>>> +                       debug("DIS ");
>>>> +               }
>>>> +               debug("RX Flow ");
>>>> +               if (cap & FLOW_CTRL_RX) {
>>>> +                       *flow |= FLOW_CR_RX_FCEN;
>>>> +                       debug("EN");
>>>> +               } else {
>>>> +                       debug("DIS");
>>>> +               }
>>>> +       }
>>>> +       debug("\n");
>>>> +       return 0;
>>>> +}
>>>
>>>I see where Marek is coming from wrt thisall being in phylib already.
>>>I guess you always have a fixed phy internal, so there's no need of the
>>>flexibility of phylib. Maybe there's at least opportunity to
>>>consolidate subroutines even if not using phylib the normal way.
>>
>>I am a bit confused what you say. Do you mean that I can keep the current
>>code as is in this area?
>>Please confirm?
>>The access to PHY also needs our own register read/write through USB, so
>>using the phylib is more complicated.

The thought here is to do a minor refactor of the phy.c code such that
all this parsing of the bits can be shared code, while access to the
MDIO interface is specialized for your driver.

>>>> +
>>>> +int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
>>>> +                   struct ueth_data *ss) {
>>>> +       struct usb_interface *iface;
>>>> +       struct usb_interface_descriptor *iface_desc;
>>>> +       int i;
>>>> +
>>>> +       iface = &dev->config.if_desc[ifnum];
>>>> +       iface_desc = &dev->config.if_desc[ifnum].desc;
>>>> +
>>>> +       memset(ss, '\0', sizeof(struct ueth_data));
>>>> +
>>>> +       /* Initialize the ueth_data structure with some useful info */
>>>> +       ss->ifnum = ifnum;
>>>> +       ss->pusb_dev = dev;
>>>> +       ss->subclass = iface_desc->bInterfaceSubClass;
>>>> +       ss->protocol = iface_desc->bInterfaceProtocol;
>>>> +
>>>> +       /*
>>>> +        * We are expecting a minimum of 3 endpoints
>>>> +        * - in, out (bulk), and int.
>>>> +        * We will ignore any others.
>>>> +        */
>>>> +       for (i = 0; i < iface_desc->bNumEndpoints; i++) {
>>>> +               /* is it an BULK endpoint? */
>>>> +               if ((iface->ep_desc[i].bmAttributes &
>>>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>>>USB_ENDPOINT_XFER_BULK) {
>>>> +                       if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
>>>> +                               ss->ep_in =
>>>> +                                       iface->ep_desc[i].bEndpointAddress &
>>>> +                                       USB_ENDPOINT_NUMBER_MASK;
>>>> +                       else
>>>> +                               ss->ep_out =
>>>> +                                       iface->ep_desc[i].bEndpointAddress &
>>>> +                                       USB_ENDPOINT_NUMBER_MASK;
>>>> +               }
>>>> +
>>>> +               /* is it an interrupt endpoint? */
>>>> +               if ((iface->ep_desc[i].bmAttributes &
>>>> +                       USB_ENDPOINT_XFERTYPE_MASK) ==
>>>USB_ENDPOINT_XFER_INT) {
>>>> +                       ss->ep_int = iface->ep_desc[i].bEndpointAddress &
>>>> +                               USB_ENDPOINT_NUMBER_MASK;
>>>> +                       ss->irqinterval = iface->ep_desc[i].bInterval;
>>>> +               }
>>>> +       }
>>>> +       debug("Endpoints In %d Out %d Int %d\n",
>>>> +             ss->ep_in, ss->ep_out, ss->ep_int);
>>>> +
>>>> +       /* Do some basic sanity checks, and bail if we find a problem */
>>>> +       if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
>>>> +           !ss->ep_in || !ss->ep_out || !ss->ep_int) {
>>>> +               debug("Problems with device\n");
>>>> +               return 0;
>>>
>>>Seems this should return an error.
>>
>>The caller probe_valid_drivers() in usb_ether.c expects 0 for skipping a bad
>>device.

OK.

>>>> +       }
>>>> +       dev->privptr = (void *)ss;
>>>> +
>>>> +#ifndef CONFIG_DM_ETH
>>>> +       /* alloc driver private */
>>>> +       ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
>>>> +       if (!ss->dev_priv)
>>>> +               return 0;
>>>> +#endif
>>>> +
>>>> +       return 1;
>>>> +}
>>>> +
>>
>>Thank you.
>>Yuiko
>>_______________________________________________
>>U-Boot mailing list
>>U-Boot at lists.denx.de
>>https://lists.denx.de/listinfo/u-boot
>
> Joe,
> Please reply so that I can re-submit the patch.
> Thank you in advance.
> Best regards,
> Yuiko

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2017-05-30 16:54 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-10 19:23 [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx Yuiko.Oshino at microchip.com
2017-04-14 12:35 ` Marek Vasut
2017-04-18 15:58   ` Yuiko.Oshino at microchip.com
2017-04-18 16:47     ` Marek Vasut
2017-05-03 14:20       ` Yuiko.Oshino at microchip.com
2017-04-18 18:18 ` Simon Glass
2017-05-05 20:58 ` Joe Hershberger
2017-05-10 15:25   ` Yuiko.Oshino at microchip.com
2017-05-24 15:14     ` Yuiko.Oshino at microchip.com
2017-05-30 16:54       ` Joe Hershberger
2017-05-25 16:43     ` Joe Hershberger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.