[U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx

Joe Hershberger joe.hershberger at gmail.com
Fri May 5 20:58:38 UTC 2017


Hi Yuiko,

On Mon, Apr 10, 2017 at 2:23 PM,  <Yuiko.Oshino at microchip.com> wrote:
> From: Yuiko Oshino <yuiko.oshino at microchip.com>
>
> Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
>
> Signed-off-by: Yuiko Oshino <yuiko.oshino at microchip.com>
> Cc: Marek Vasut <marex at 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


More information about the U-Boot mailing list