Hi Yuiko, On Fri, Jul 14, 2017 at 10:29 AM, <yuiko.osh...@microchip.com> wrote: > From: Yuiko Oshino <yuiko.osh...@microchip.com> > > Add support for Microchip LAN7500, LAN7800 and LAN7850, USB to 10/100/1000 > Ethernet Controllers > > Signed-off-by: Yuiko Oshino <yuiko.osh...@microchip.com> > Cc: Marek Vasut <ma...@denx.de> > Cc: Joe Hershberger <joe.hershber...@gmail.com> > --- > Changes for v2: > - wait_for_bit functions copy the real one. > - uses phylib > - unnecessary variables were removed > - all return values are checked > - uses mii_resolve_flowctrl_fdx() from linux/mii.h ([PATCH] mii: Add > mii_resolve_flowctrl_fdx() submitted on June 26, 2017) > --- > drivers/usb/Kconfig | 2 + > drivers/usb/eth/Kconfig | 18 ++ > drivers/usb/eth/Makefile | 2 + > drivers/usb/eth/lan75xx.c | 320 +++++++++++++++++++++++++++++ > drivers/usb/eth/lan78xx.c | 479 +++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/eth/lan7x.c | 506 > ++++++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/eth/lan7x.h | 232 +++++++++++++++++++++ > 7 files changed, 1559 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..8ac7784 > --- /dev/null > +++ b/drivers/usb/eth/lan75xx.c > @@ -0,0 +1,320 @@ > +/* > + * 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" > + > +/* LAN75xx specific register/bit defines */ > +#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) > + > +/* > + * 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, 1); > + if (ret) > + return ret; > + > + /* phy reset */ > + 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, LAN75XX_FCT_FLOW, fct_flow); > + if (ret) > + return ret; > + return lan7x_write_reg(udev, FLOW, flow); > +} > + > +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_set_receive_filter(struct usb_device *udev) > +{ > + /* No multicast in u-boot */ > + return lan7x_write_reg(udev, LAN75XX_RFE_CTL, > + RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT); > +} > + > +/* starts the TX path */ > +static void lan75xx_start_tx_path(struct usb_device *udev) > +{ > + /* Enable Tx at MAC */ > + lan7x_write_reg(udev, MAC_TX, MAC_TX_TXEN); > + > + /* Enable Tx at SCSRs */ > + lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, FCT_TX_CTL_EN); > +} > + > +/* Starts the Receive path */ > +static void lan75xx_start_rx_path(struct usb_device *udev) > +{ > + /* Enable Rx at MAC */ > + lan7x_write_reg(udev, MAC_RX, > + LAN7X_MAC_RX_MAX_SIZE_DEFAULT | > + MAC_RX_FCS_STRIP | MAC_RX_RXEN); > + > + /* Enable Rx at SCSRs */ > + lan7x_write_reg(udev, LAN75XX_FCT_RX_CTL, FCT_RX_CTL_EN); > +} > + > +static int lan75xx_basic_reset(struct usb_device *udev, > + struct ueth_data *dev, > + struct lan7x_private *priv) > +{ > + int ret; > + u32 val; > + > + ret = lan7x_basic_reset(udev, dev); > + if (ret) > + return ret; > + > + /* Keep the chip ID */ > + ret = lan7x_read_reg(udev, ID_REV, &val); > + if (ret) > + return ret; > + debug("LAN75xx ID_REV = 0x%08x\n", val); > + > + priv->chipid = (val & ID_REV_CHIP_ID_MASK) >> 16; > + > + /* Respond to the IN token with a NAK */ > + ret = lan7x_read_reg(udev, HW_CFG, &val); > + if (ret) > + return ret; > + val |= LAN75XX_HW_CFG_BIR; > + return lan7x_write_reg(udev, HW_CFG, val); > +} > + > +#ifdef CONFIG_DM_ETH > +int lan75xx_write_hwaddr(struct udevice *dev) > +{ > + struct usb_device *udev = dev_get_parent_priv(dev); > + struct eth_pdata *pdata = dev_get_platdata(dev); > + unsigned char *enetaddr = pdata->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); > + > + return 0; > +} > + > +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 ueth_data *ueth = &priv->ueth; > + int ret; > + u32 write_buf; > + > + /* Reset and read Mac addr were done in probe() */ > + ret = lan75xx_write_hwaddr(dev); > + 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_receive_filter(udev); > + if (ret) > + return ret; > + > + /* phy workaround for gig link */ > + ret = lan75xx_phy_gig_workaround(udev, ueth); > + if (ret) > + return ret; > + > + /* Init PHY, autonego, and link */ > + ret = lan7x_eth_phylib_connect(dev, &priv->ueth); > + if (ret) > + return ret; > + ret = lan7x_eth_phylib_config_start(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); > + > + return lan75xx_update_flowcontrol(udev, ueth); > +} > + > +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); > + int ret; > + > + /* 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 */ > + > + ret = usb_ether_register(dev, ueth, RX_URB_SIZE); > + if (ret) > + return ret; > + > + /* Register phylib */ > + return lan7x_phylib_register(dev); > +} > + > +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, > + .remove = lan7x_eth_remove, > + .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..5160db7 > --- /dev/null > +++ b/drivers/usb/eth/lan78xx.c > @@ -0,0 +1,479 @@ > +/* > + * 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 > + > +/* > + * 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); > + if (ret) > + return ret; > + > + ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_PWR_DN_PWRDN_N", > + LAN78XX_OTP_PWR_DN, > + LAN78XX_OTP_PWR_DN_PWRDN_N, > + false, 1000, 0); > + 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); > + if (ret) > + return ret; > + ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2, > + ((offset + i) & > LAN78XX_OTP_ADDR2_10_3)); > + if (ret) > + return ret; > + > + ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD, > + LAN78XX_OTP_FUNC_CMD_READ); > + if (ret) > + return ret; > + 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, 0); > + if (ret) > + return ret; > + > + ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf); > + if (ret) > + return ret; > + > + 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); > + if (ret) > + return ret; > + } > + 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 -EINVAL; > +} > + > +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, LAN78XX_FCT_FLOW, fct_flow); > + if (ret) > + return ret; > + return lan7x_write_reg(udev, FLOW, flow); > +} > + > +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); > + if (ret) > + return ret; > + 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 (ret) > + return ret; > + } > + /* if the EEPROM mac address is good, then exit */ > + if (done) > + return 0; > + > + /* try reading mac address from OTP if the device is LAN78xx */ > + return lan78xx_read_otp_mac(enetaddr, udev); > +} > + > +static int lan78xx_set_receive_filter(struct usb_device *udev) > +{ > + /* No multicast in u-boot for now */ > + return lan7x_write_reg(udev, LAN78XX_RFE_CTL, > + RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT); > +} > + > +/* starts the TX path */ > +static void lan78xx_start_tx_path(struct usb_device *udev) > +{ > + /* Enable Tx at MAC */ > + lan7x_write_reg(udev, MAC_TX, MAC_TX_TXEN); > + > + /* Enable Tx at SCSRs */ > + lan7x_write_reg(udev, LAN78XX_FCT_TX_CTL, FCT_TX_CTL_EN); > +} > + > +/* Starts the Receive path */ > +static void lan78xx_start_rx_path(struct usb_device *udev) > +{ > + /* Enable Rx at MAC */ > + lan7x_write_reg(udev, MAC_RX, > + LAN7X_MAC_RX_MAX_SIZE_DEFAULT | > + MAC_RX_FCS_STRIP | MAC_RX_RXEN); > + > + /* Enable Rx at SCSRs */ > + lan7x_write_reg(udev, LAN78XX_FCT_RX_CTL, FCT_RX_CTL_EN); > +} > + > +static int lan78xx_basic_reset(struct usb_device *udev, > + struct ueth_data *dev, > + struct lan7x_private *priv) > +{ > + int ret; > + u32 val; > + > + ret = lan7x_basic_reset(udev, dev); > + if (ret) > + return ret; > + > + /* Keep the chip ID */ > + ret = lan7x_read_reg(udev, ID_REV, &val); > + if (ret) > + return ret; > + debug("LAN78xx ID_REV = 0x%08x\n", val); > + > + priv->chipid = (val & ID_REV_CHIP_ID_MASK) >> 16; > + > + /* Respond to the IN token with a NAK */ > + ret = lan7x_read_reg(udev, LAN78XX_USB_CFG0, &val); > + if (ret) > + return ret; > + val |= LAN78XX_USB_CFG0_BIR; > + return lan7x_write_reg(udev, LAN78XX_USB_CFG0, val); > +} > + > +#ifdef CONFIG_DM_ETH > +int lan78xx_write_hwaddr(struct udevice *dev) > +{ > + struct usb_device *udev = dev_get_parent_priv(dev); > + struct eth_pdata *pdata = dev_get_platdata(dev); > + unsigned char *enetaddr = pdata->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); > + > + return 0; > +} > + > +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); > + > + int ret; > + u32 write_buf; > + > + /* Reset and read Mac addr were done in probe() */ > + ret = lan78xx_write_hwaddr(dev); > + 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 */ > + ret = lan7x_write_reg(udev, LAN78XX_FCT_RX_FIFO_END, > + (MAX_RX_FIFO_SIZE - 512) / 512); > + if (ret) > + return ret; > + > + ret = lan7x_write_reg(udev, LAN78XX_FCT_TX_FIFO_END, > + (MAX_TX_FIFO_SIZE - 512) / 512); > + 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_receive_filter(udev); > + if (ret) > + return ret; > + > + /* Init PHY, autonego, and link */ > + ret = lan7x_eth_phylib_connect(dev, &priv->ueth); > + if (ret) > + return ret; > + ret = lan7x_eth_phylib_config_start(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; > + > + lan78xx_start_tx_path(udev); > + lan78xx_start_rx_path(udev); > + > + return lan78xx_update_flowcontrol(udev, &priv->ueth); > +} > + > +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); > + int ret; > + > + /* 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 */ > + > + ret = usb_ether_register(dev, ueth, RX_URB_SIZE); > + if (ret) > + return ret; > + > + /* Register phylib */ > + return lan7x_phylib_register(dev); > +} > + > +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, > + .remove = lan7x_eth_remove, > + .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..a2ebbeb > --- /dev/null > +++ b/drivers/usb/eth/lan7x.c > @@ -0,0 +1,506 @@ > +/* > + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <dm.h> > +#include <malloc.h> > +#include <miiphy.h> > +#include <memalign.h> > +#include <usb.h> > +#include <linux/ethtool.h> > +#include <linux/mii.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; > +} > + > +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, 0); > +} > + > +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 val & 0xFFFF; > +} > + > +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, int > regval) > +{ > + u32 addr; > + > + /* confirm MII not busy */ > + if (lan7x_phy_wait_not_busy(udev)) { > + debug("MII is busy in %s\n", __func__); > + return; > + } > + > + lan7x_write_reg(udev, MII_DATA, regval); > + > + /* 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); > +} > + > +/* > + * Lan7x phylib wrappers > + */ > +static int lan7x_phylib_mdio_read(struct mii_dev *bus, > + int addr, int devad, int reg) > +{ > + struct usb_device *udev = dev_get_parent_priv(bus->priv); > + > + return lan7x_mdio_read(udev, addr, reg); > +} > + > +static int lan7x_phylib_mdio_write(struct mii_dev *bus, > + int addr, int devad, int reg, u16 val) > +{ > + struct usb_device *udev = dev_get_parent_priv(bus->priv); > + > + lan7x_mdio_write(udev, addr, reg, (int)val); > + > + return 0; > +} > + > +/* > + * Lan7x eeprom functions > + */ > +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, 0); > +} > + > +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, 0); > +} > + > +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; > +} > + > +/* > + * Lan7x phylib functions > + */ > +int lan7x_phylib_register(struct udevice *udev) > +{ > + struct lan7x_private *priv = dev_get_priv(udev); > + int ret; > + > + priv->mdiobus = mdio_alloc(); > + if (!priv->mdiobus) { > + printf("mdio_alloc failed\n"); > + return -ENOMEM; > + } > + priv->mdiobus->read = lan7x_phylib_mdio_read; > + priv->mdiobus->write = lan7x_phylib_mdio_write; > + strcpy(priv->mdiobus->name, "lan78x_mdiobus"); > + priv->mdiobus->priv = (void *)udev; > + > + ret = mdio_register(priv->mdiobus); > + if (ret) { > + printf("mdio_register failed\n"); > + free(priv->mdiobus); > + return -ENOMEM; > + } > + > + return 0; > +} > + > +int lan7x_eth_phylib_connect(struct udevice *udev, struct ueth_data *dev) > +{ > + struct lan7x_private *priv = dev_get_priv(udev); > + > + priv->phydev = phy_connect(priv->mdiobus, dev->phy_id, > + udev, PHY_INTERFACE_MODE_MII); > + > + if (!priv->phydev) { > + printf("phy_connect failed\n"); > + return -ENODEV; > + } > + return 0; > +} > + > +int lan7x_eth_phylib_config_start(struct udevice *udev) > +{ > + struct lan7x_private *priv = dev_get_priv(udev); > + int ret; > + > + /* configure supported modes */ > + priv->phydev->supported = PHY_BASIC_FEATURES | > + SUPPORTED_1000baseT_Full | > + SUPPORTED_Pause | > + SUPPORTED_Asym_Pause; > + > + priv->phydev->advertising = ADVERTISED_10baseT_Half | > + ADVERTISED_10baseT_Full | > + ADVERTISED_100baseT_Half | > + ADVERTISED_100baseT_Full | > + ADVERTISED_1000baseT_Full | > + ADVERTISED_Pause | > + ADVERTISED_Asym_Pause | > + ADVERTISED_Autoneg; > + > + priv->phydev->autoneg = AUTONEG_ENABLE; > + > + ret = genphy_config_aneg(priv->phydev); > + if (ret) { > + printf("genphy_config_aneg failed\n"); > + return ret; > + } > + ret = phy_startup(priv->phydev); > + if (ret) { > + printf("phy_startup failed\n"); > + return ret; > + } > + > + debug("** %s() speed %i duplex %i adv %X supp %X\n", __func__, > + priv->phydev->speed, priv->phydev->duplex, > + priv->phydev->advertising, priv->phydev->supported); > + > + return 0; > +} > + > +int lan7x_update_flowcontrol(struct usb_device *udev, > + struct ueth_data *dev, > + uint32_t *flow, uint32_t *fct_flow) > +{ > + uint32_t lcladv, rmtadv; > + u8 cap = 0; > + struct lan7x_private *priv = dev_get_priv(udev->dev); > + > + debug("** %s()\n", __func__); > + debug("** %s() priv->phydev->speed %i duplex %i\n", __func__, > + priv->phydev->speed, priv->phydev->duplex); > + > + if (priv->phydev->duplex == DUPLEX_FULL) { > + lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE); > + rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA); > + cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); > + > + 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 -EINVAL; > +} > + > +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); > + if (ret) > + return ret; > + 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, 0); /* could take over 125mS */ > + if (ret) > + return ret; > + > + return lan7x_wait_for_bit(udev, "PMT_CTL_READY", > + PMT_CTL, PMT_CTL_READY, > + true, 1000, 0); > +} > + > +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, 0); > + if (ret) > + return ret; > + > + return lan7x_pmt_phy_reset(udev, dev); > +} > + > +#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); > + struct ueth_data *ueth = &priv->ueth; > + 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(ueth->pusb_dev, > + usb_sndbulkpipe(ueth->pusb_dev, ueth->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_eth_recv(struct udevice *dev, int flags, uchar **packetp) > +{ > + struct lan7x_private *priv = dev_get_priv(dev); > + struct ueth_data *ueth = &priv->ueth; > + 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(ueth->pusb_dev, > + usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), > + (void *)recv_buf, RX_URB_SIZE, &actual_len, > + USB_BULK_RECV_TIMEOUT_MS); > + > + 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);
This is not correct. See eth_rx in net/net-uclass.c You are expected to return the number of bytes written to the packetp buffer. The framework will then handle the packed for you and call your free_pkt() function. > + } > + > + return err; > +} > + > +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; > +} > + > +int lan7x_eth_remove(struct udevice *dev) > +{ > + struct lan7x_private *priv = dev_get_priv(dev); > + > + debug("** %s()\n", __func__); > + free(priv->phydev); > + mdio_unregister(priv->mdiobus); > + mdio_free(priv->mdiobus); > + > + 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..6f4647a > --- /dev/null > +++ b/drivers/usb/eth/lan7x.h > @@ -0,0 +1,232 @@ > +/* > + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <console.h> > +#include <watchdog.h> > + > +/* 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 { > + struct ueth_data ueth; > + u32 chipid; /* Chip or device ID */ > + struct mii_dev *mdiobus; > + struct phy_device *phydev; > +}; > + > +/* > + * 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); > + > +static inline int lan7x_wait_for_bit(struct usb_device *udev, > + const char *prefix, const u32 reg, > + const u32 mask, const bool set, > + const unsigned int timeout_ms, > + const bool breakable) > +{ > + u32 val; > + unsigned long start = get_timer(0); > + > + while (1) { > + lan7x_read_reg(udev, reg, &val); > + > + if (!set) > + val = ~val; > + > + if ((val & mask) == mask) > + return 0; > + > + if (get_timer(start) > timeout_ms) > + break; > + > + if (breakable && ctrlc()) { > + puts("Abort\n"); > + return -EINTR; > + } > + > + udelay(1); > + WATCHDOG_RESET(); > + } > + > + debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n", prefix, reg, > + mask, set); > + > + return -ETIMEDOUT; > +} > + > +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx); > + > +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, > + int regval); > + > +static inline int lan7x_mdio_wait_for_bit(struct usb_device *udev, > + const char *prefix, > + int phy_id, const u32 reg, > + const u32 mask, const bool set, > + const unsigned int timeout_ms, > + const bool breakable) > +{ > + u32 val; > + unsigned long start = get_timer(0); > + > + while (1) { > + val = lan7x_mdio_read(udev, phy_id, reg); > + > + if (!set) > + val = ~val; > + > + if ((val & mask) == mask) > + return 0; > + > + if (get_timer(start) > timeout_ms) > + break; > + > + if (breakable && ctrlc()) { > + puts("Abort\n"); > + return -EINTR; > + } > + > + udelay(1); > + WATCHDOG_RESET(); > + } > + > + debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n", prefix, reg, > + mask, set); > + > + return -ETIMEDOUT; > +} > + > +int lan7x_phylib_register(struct udevice *udev); > + > +int lan7x_eth_phylib_connect(struct udevice *udev, struct ueth_data *dev); > + > +int lan7x_eth_phylib_config_start(struct udevice *udev); > + > +int lan7x_pmt_phy_reset(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); > + > +#ifdef CONFIG_DM_ETH This should always be the case on a new driver. Anything that is supporting non-DM should be dropped from the patch. > +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); > + > +int lan7x_eth_remove(struct udevice *dev); > +#endif /* CONFIG_DM_ETH */ > -- > 2.7.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot