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

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

On 04/10/2017 09:23 PM, Yuiko.Oshino@microchip.com wrote:
From: Yuiko Oshino yuiko.oshino@microchip.com
Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
Signed-off-by: Yuiko Oshino yuiko.oshino@microchip.com Cc: Marek Vasut marex@denx.de
Hi!
mostly nit-picking below, thanks for the hard work!
+/* MAC ADDRESS PERFECT FILTER For LAN75xx */ +#define LAN75XX_ADDR_FILTX 0x300 +#define LAN75XX_ADDR_FILTX_FB_VALID BIT(31)
+#ifndef CONFIG_DM_ETH
I'd just make this depend on DM and scrap the non-DM part. It's not worth the hassle .
+/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */
+/* local defines */ +#define LAN75XX_BASE_NAME "lan75xx" +#endif
+/*
- Lan75xx infrastructure commands
- */
[...]
diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c new file mode 100644 index 0000000..e450f2c --- /dev/null +++ b/drivers/usb/eth/lan78xx.c
[...]
+#define LAN78XX_MAF_BASE 0x400 +#define LAN78XX_MAF_HIX 0x00 +#define LAN78XX_MAF_LOX 0x04 +#define LAN78XX_MAF_HI_BEGIN (LAN78XX_MAF_BASE + LAN78XX_MAF_HIX) +#define LAN78XX_MAF_LO_BEGIN (LAN78XX_MAF_BASE + LAN78XX_MAF_LOX) +#define LAN78XX_MAF_HI(index) (LAN78XX_MAF_BASE + (8 * (index)) + \
(LAN78XX_MAF_HIX))
+#define LAN78XX_MAF_LO(index) (LAN78XX_MAF_BASE + (8 * (index)) + \
(LAN78XX_MAF_LOX))
You don't need the extra parenthesis around LAN78XX_MAX_LOF (dtto for MAF_HIX above).
+#define LAN78XX_MAF_HI_VALID BIT(31)
[...]
+#ifndef CONFIG_DM_ETH
DTTO here, just dump the non-DM case :)
+/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */
+/* local defines */ +#define LAN78XX_BASE_NAME "lan78xx" +#endif
[...]
+static const struct lan7x_dongle lan78xx_dongles[] = {
- {0x0424, 0x7800}, /* LAN7800 USB Ethernet */
- {0x0424, 0x7850}, /* LAN7850 USB Ethernet */
- {0x0000, 0x0000} /* END - Do not remove */
+};
You can use ARRAY_SIZE() to save a few bytes maybe ?
+/* Probe to see if a new device is actually an Microchip device */ +int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss)
+{
- int i;
- /* let's examine the device now */
- for (i = 0; lan78xx_dongles[i].vendor != 0; i++) {
if (dev->descriptor.idVendor == lan78xx_dongles[i].vendor &&
dev->descriptor.idProduct == lan78xx_dongles[i].product)
/* Found a supported dongle */
break;
- }
- if (lan78xx_dongles[i].vendor == 0)
return 0;
- /* At this point, we know we've got a live one */
- debug("\n\nUSB Ethernet device LAN78xx detected\n");
- /*
* Note that this function needs to return 1
* for success
*/
- return lan7x_eth_probe(dev, ifnum, ss);
+}
[...]
+int lan7x_update_flowcontrol(struct usb_device *udev,
struct ueth_data *dev,
uint32_t *flow, uint32_t *fct_flow)
+{
- uint32_t lcladv, rmtadv, ctrl1000, stat1000;
- uint32_t advertising = 0, lp_advertising = 0, nego = 0;
- uint32_t duplex = 0;
- u8 cap = 0;
Shouldn't this be split into drivers/net/phy and be part of phylib ? This code looks kinda familiar and similar to what I saw there ...
- lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
- advertising = lan7x_mii_get_an(lcladv);
- rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
- lp_advertising = lan7x_mii_get_an(rmtadv);
- ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
- stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
- if (ctrl1000 & ADVERTISE_1000HALF)
advertising |= ADVERTISED_1000baseT_Half;
- if (ctrl1000 & ADVERTISE_1000FULL)
advertising |= ADVERTISED_1000baseT_Full;
- if (stat1000 & LPA_1000HALF)
lp_advertising |= ADVERTISED_1000baseT_Half;
- if (stat1000 & LPA_1000FULL)
lp_advertising |= ADVERTISED_1000baseT_Full;
- nego = advertising & lp_advertising;
- debug("LAN7x linked at ");
- if (nego & (ADVERTISED_1000baseT_Full | ADVERTISED_1000baseT_Half)) {
debug("1000 ");
duplex = !!(nego & ADVERTISED_1000baseT_Full);
- } else if (nego & (ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half)) {
debug("100 ");
duplex = !!(nego & ADVERTISED_100baseT_Full);
- } else {
debug("10 ");
duplex = !!(nego & ADVERTISED_10baseT_Full);
- }
- if (duplex == DUPLEX_FULL)
debug("full dup ");
- else
debug("half dup ");
- if (duplex == DUPLEX_FULL) {
if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
if (lcladv & ADVERTISE_PAUSE_CAP)
cap = FLOW_CTRL_RX;
else if (rmtadv & LPA_PAUSE_CAP)
cap = FLOW_CTRL_TX;
}
debug("TX Flow ");
if (cap & FLOW_CTRL_TX) {
*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
/* set fct_flow thresholds to 20% and 80% */
*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
& 0x7FUL);
The outermost parenthesis are not needed :)
*fct_flow <<= 8UL;
*fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
& 0x7FUL);
debug("EN ");
} else {
debug("DIS ");
}
debug("RX Flow ");
if (cap & FLOW_CTRL_RX) {
*flow |= FLOW_CR_RX_FCEN;
debug("EN");
} else {
debug("DIS");
}
- }
- debug("\n");
- return 0;
+}
[...]
+int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss)
+{
- struct usb_interface *iface;
- struct usb_interface_descriptor *iface_desc;
- int i;
- iface = &dev->config.if_desc[ifnum];
- iface_desc = &dev->config.if_desc[ifnum].desc;
- memset(ss, '\0', sizeof(struct ueth_data));
- /* Initialize the ueth_data structure with some useful info */
- ss->ifnum = ifnum;
- ss->pusb_dev = dev;
- ss->subclass = iface_desc->bInterfaceSubClass;
- ss->protocol = iface_desc->bInterfaceProtocol;
- /*
* We are expecting a minimum of 3 endpoints
* - in, out (bulk), and int.
* We will ignore any others.
*/
- for (i = 0; i < iface_desc->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else
ss->ep_out =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
/* is it an interrupt endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
ss->ep_int = iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->irqinterval = iface->ep_desc[i].bInterval;
}
- }
- debug("Endpoints In %d Out %d Int %d\n",
ss->ep_in, ss->ep_out, ss->ep_int);
- /* Do some basic sanity checks, and bail if we find a problem */
- if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
!ss->ep_in || !ss->ep_out || !ss->ep_int) {
debug("Problems with device\n");
return 0;
- }
- dev->privptr = (void *)ss;
+#ifndef CONFIG_DM_ETH
- /* alloc driver private */
- ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
- if (!ss->dev_priv)
return 0;
+#endif
- return 1;
return 1 looks a bit weird, but maybe that's correct here .
+}
[...]
+/* Some extra defines */ +#define LAN7X_INTERNAL_PHY_ID 1
+#define LAN7X_MAC_RX_MAX_SIZE(mtu) \
- ((mtu) << 16) /**< Max frame size */
Use just one asterisk in the comment please.
+#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \
- LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */)
+/* Timeouts */ +#define USB_CTRL_SET_TIMEOUT_MS 5000 +#define USB_CTRL_GET_TIMEOUT_MS 5000 +#define USB_BULK_SEND_TIMEOUT_MS 5000 +#define USB_BULK_RECV_TIMEOUT_MS 5000 +#define TIMEOUT_RESOLUTION_MS 50 +#define PHY_CONNECT_TIMEOUT_MS 5000
+#define RX_URB_SIZE 2048
+/* driver private */ +struct lan7x_private { +#ifdef CONFIG_DM_ETH
- struct ueth_data ueth;
+#endif
- int have_hwaddr; /* 1 if we have a hardware MAC address */
- u32 chipid; /* Chip or device ID */
+};
[...]

From: Yuiko Oshino yuiko.oshino@microchip.com
-----Original Message----- From: Marek Vasut [mailto:marex@denx.de] Sent: Friday, April 14, 2017 8:35 AM To: Yuiko Oshino - C18177; u-boot@lists.denx.de Subject: Re: [PATCH] Add support for Microchip LAN75xx and LAN78xx
On 04/10/2017 09:23 PM, Yuiko.Oshino@microchip.com wrote:
From: Yuiko Oshino yuiko.oshino@microchip.com
Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
Signed-off-by: Yuiko Oshino yuiko.oshino@microchip.com Cc: Marek Vasut marex@denx.de
Hi!
mostly nit-picking below, thanks for the hard work!
Marek! Thank you for your hard work too!
+/* MAC ADDRESS PERFECT FILTER For LAN75xx */ +#define LAN75XX_ADDR_FILTX 0x300 +#define LAN75XX_ADDR_FILTX_FB_VALID BIT(31)
+#ifndef CONFIG_DM_ETH
I'd just make this depend on DM and scrap the non-DM part. It's not worth the hassle .
Okay, please let me confirm that I can delete all #ifndef CONFIG_DM_ETH stuff and remove non-DM code? Do I still need to keep #ifdef CONFIG_DM_ETH to avoid build errors?
+/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */
+/* local defines */ +#define LAN75XX_BASE_NAME "lan75xx" +#endif
+/*
- Lan75xx infrastructure commands
- */
[...]
diff --git a/drivers/usb/eth/lan78xx.c b/drivers/usb/eth/lan78xx.c new file mode 100644 index 0000000..e450f2c --- /dev/null +++ b/drivers/usb/eth/lan78xx.c
[...]
+#define LAN78XX_MAF_BASE 0x400 +#define LAN78XX_MAF_HIX 0x00 +#define LAN78XX_MAF_LOX 0x04 +#define LAN78XX_MAF_HI_BEGIN (LAN78XX_MAF_BASE +
LAN78XX_MAF_HIX)
+#define LAN78XX_MAF_LO_BEGIN (LAN78XX_MAF_BASE +
LAN78XX_MAF_LOX)
+#define LAN78XX_MAF_HI(index) (LAN78XX_MAF_BASE + (8 *
(index)) + \
(LAN78XX_MAF_HIX))
+#define LAN78XX_MAF_LO(index) (LAN78XX_MAF_BASE + (8 *
(index)) + \
(LAN78XX_MAF_LOX))
You don't need the extra parenthesis around LAN78XX_MAX_LOF (dtto for MAF_HIX above).
Thank you. Will remove the extras.
+#define LAN78XX_MAF_HI_VALID BIT(31)
[...]
+#ifndef CONFIG_DM_ETH
DTTO here, just dump the non-DM case :)
+/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */
+/* local defines */ +#define LAN78XX_BASE_NAME "lan78xx" +#endif
[...]
+static const struct lan7x_dongle lan78xx_dongles[] = {
- {0x0424, 0x7800}, /* LAN7800 USB Ethernet */
- {0x0424, 0x7850}, /* LAN7850 USB Ethernet */
- {0x0000, 0x0000} /* END - Do not remove */
+};
You can use ARRAY_SIZE() to save a few bytes maybe ?
Okay, I just followed what all other USB to Ether drivers do. But I can remove the "Do not remove" entry to save you a few and use the ARRAY_SIZE.
+/* Probe to see if a new device is actually an Microchip device */ +int lan78xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss)
+{
- int i;
- /* let's examine the device now */
- for (i = 0; lan78xx_dongles[i].vendor != 0; i++) {
if (dev->descriptor.idVendor == lan78xx_dongles[i].vendor
&&
dev->descriptor.idProduct == lan78xx_dongles[i].product)
/* Found a supported dongle */
break;
- }
- if (lan78xx_dongles[i].vendor == 0)
return 0;
- /* At this point, we know we've got a live one */
- debug("\n\nUSB Ethernet device LAN78xx detected\n");
- /*
* Note that this function needs to return 1
* for success
*/
- return lan7x_eth_probe(dev, ifnum, ss);
+}
[...]
+int lan7x_update_flowcontrol(struct usb_device *udev,
struct ueth_data *dev,
uint32_t *flow, uint32_t *fct_flow)
+{
- uint32_t lcladv, rmtadv, ctrl1000, stat1000;
- uint32_t advertising = 0, lp_advertising = 0, nego = 0;
- uint32_t duplex = 0;
- u8 cap = 0;
Shouldn't this be split into drivers/net/phy and be part of phylib ? This code looks kinda familiar and similar to what I saw there ...
All I need is to get the auto negotiated duplex mode so that I can set the flow control. I am going to use our device specific status registers instead of the standard registers to get the duplex status to simplify the situation.
- lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
- advertising = lan7x_mii_get_an(lcladv);
- rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
- lp_advertising = lan7x_mii_get_an(rmtadv);
- ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
- stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
- if (ctrl1000 & ADVERTISE_1000HALF)
advertising |= ADVERTISED_1000baseT_Half;
- if (ctrl1000 & ADVERTISE_1000FULL)
advertising |= ADVERTISED_1000baseT_Full;
- if (stat1000 & LPA_1000HALF)
lp_advertising |= ADVERTISED_1000baseT_Half;
- if (stat1000 & LPA_1000FULL)
lp_advertising |= ADVERTISED_1000baseT_Full;
- nego = advertising & lp_advertising;
- debug("LAN7x linked at ");
- if (nego & (ADVERTISED_1000baseT_Full |
ADVERTISED_1000baseT_Half)) {
debug("1000 ");
duplex = !!(nego & ADVERTISED_1000baseT_Full);
- } else if (nego & (ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half)) {
debug("100 ");
duplex = !!(nego & ADVERTISED_100baseT_Full);
- } else {
debug("10 ");
duplex = !!(nego & ADVERTISED_10baseT_Full);
- }
- if (duplex == DUPLEX_FULL)
debug("full dup ");
- else
debug("half dup ");
- if (duplex == DUPLEX_FULL) {
if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
if (lcladv & ADVERTISE_PAUSE_CAP)
cap = FLOW_CTRL_RX;
else if (rmtadv & LPA_PAUSE_CAP)
cap = FLOW_CTRL_TX;
}
debug("TX Flow ");
if (cap & FLOW_CTRL_TX) {
*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
/* set fct_flow thresholds to 20% and 80% */
*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
& 0x7FUL);
The outermost parenthesis are not needed :)
Thank you again!
*fct_flow <<= 8UL;
*fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
& 0x7FUL);
debug("EN ");
} else {
debug("DIS ");
}
debug("RX Flow ");
if (cap & FLOW_CTRL_RX) {
*flow |= FLOW_CR_RX_FCEN;
debug("EN");
} else {
debug("DIS");
}
- }
- debug("\n");
- return 0;
+}
[...]
+int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss)
+{
- struct usb_interface *iface;
- struct usb_interface_descriptor *iface_desc;
- int i;
- iface = &dev->config.if_desc[ifnum];
- iface_desc = &dev->config.if_desc[ifnum].desc;
- memset(ss, '\0', sizeof(struct ueth_data));
- /* Initialize the ueth_data structure with some useful info */
- ss->ifnum = ifnum;
- ss->pusb_dev = dev;
- ss->subclass = iface_desc->bInterfaceSubClass;
- ss->protocol = iface_desc->bInterfaceProtocol;
- /*
* We are expecting a minimum of 3 endpoints
* - in, out (bulk), and int.
* We will ignore any others.
*/
- for (i = 0; i < iface_desc->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK) {
if (iface->ep_desc[i].bEndpointAddress &
USB_DIR_IN)
ss->ep_in =
iface->ep_desc[i].bEndpointAddress
&
USB_ENDPOINT_NUMBER_MASK;
else
ss->ep_out =
iface->ep_desc[i].bEndpointAddress
&
USB_ENDPOINT_NUMBER_MASK;
}
/* is it an interrupt endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT) {
ss->ep_int = iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->irqinterval = iface->ep_desc[i].bInterval;
}
- }
- debug("Endpoints In %d Out %d Int %d\n",
ss->ep_in, ss->ep_out, ss->ep_int);
- /* Do some basic sanity checks, and bail if we find a problem */
- if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
!ss->ep_in || !ss->ep_out || !ss->ep_int) {
debug("Problems with device\n");
return 0;
- }
- dev->privptr = (void *)ss;
+#ifndef CONFIG_DM_ETH
- /* alloc driver private */
- ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
- if (!ss->dev_priv)
return 0;
+#endif
- return 1;
return 1 looks a bit weird, but maybe that's correct here .
Yes, the caller needs to see 1 for a successful probe.
+}
[...]
+/* Some extra defines */ +#define LAN7X_INTERNAL_PHY_ID 1
+#define LAN7X_MAC_RX_MAX_SIZE(mtu) \
- ((mtu) << 16) /**< Max frame size */
Use just one asterisk in the comment please.
Thank you.
+#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \
- LAN7X_MAC_RX_MAX_SIZE(ETH_FRAME_LEN + 4 /* VLAN */ + 4 /*
CRC */)
+/* Timeouts */ +#define USB_CTRL_SET_TIMEOUT_MS 5000 +#define USB_CTRL_GET_TIMEOUT_MS 5000 +#define USB_BULK_SEND_TIMEOUT_MS 5000 +#define USB_BULK_RECV_TIMEOUT_MS 5000 +#define TIMEOUT_RESOLUTION_MS 50 +#define PHY_CONNECT_TIMEOUT_MS 5000
+#define RX_URB_SIZE 2048
+/* driver private */ +struct lan7x_private { +#ifdef CONFIG_DM_ETH
- struct ueth_data ueth;
+#endif
- int have_hwaddr; /* 1 if we have a hardware MAC address */
- u32 chipid; /* Chip or device ID */
+};
[...]
-- Best regards, Marek Vasut
Best regards, Yuiko

On 04/18/2017 05:58 PM, Yuiko.Oshino@microchip.com wrote:
[...]
+/* MAC ADDRESS PERFECT FILTER For LAN75xx */ +#define LAN75XX_ADDR_FILTX 0x300 +#define LAN75XX_ADDR_FILTX_FB_VALID BIT(31)
+#ifndef CONFIG_DM_ETH
I'd just make this depend on DM and scrap the non-DM part. It's not worth the hassle .
Okay, please let me confirm that I can delete all #ifndef CONFIG_DM_ETH stuff and remove non-DM code? Do I still need to keep #ifdef CONFIG_DM_ETH to avoid build errors?
Make it depend on DM_ETH and be done with it. We're moving toward DM anyway.
I'm CCing Simon , so wait for his confirmation on this.
+/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */
+/* local defines */ +#define LAN75XX_BASE_NAME "lan75xx" +#endif
+/*
- Lan75xx infrastructure commands
- */
[...]
+int lan7x_update_flowcontrol(struct usb_device *udev,
struct ueth_data *dev,
uint32_t *flow, uint32_t *fct_flow)
+{
- uint32_t lcladv, rmtadv, ctrl1000, stat1000;
- uint32_t advertising = 0, lp_advertising = 0, nego = 0;
- uint32_t duplex = 0;
- u8 cap = 0;
Shouldn't this be split into drivers/net/phy and be part of phylib ? This code looks kinda familiar and similar to what I saw there ...
All I need is to get the auto negotiated duplex mode so that I can set the flow control. I am going to use our device specific status registers instead of the standard registers to get the duplex status to simplify the situation.
OK, CCing Joe, I clearly don't know enough to judge if this is good or not :)
- lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
- advertising = lan7x_mii_get_an(lcladv);
- rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
- lp_advertising = lan7x_mii_get_an(rmtadv);
- ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
- stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
- if (ctrl1000 & ADVERTISE_1000HALF)
advertising |= ADVERTISED_1000baseT_Half;
- if (ctrl1000 & ADVERTISE_1000FULL)
advertising |= ADVERTISED_1000baseT_Full;
- if (stat1000 & LPA_1000HALF)
lp_advertising |= ADVERTISED_1000baseT_Half;
- if (stat1000 & LPA_1000FULL)
lp_advertising |= ADVERTISED_1000baseT_Full;
- nego = advertising & lp_advertising;
- debug("LAN7x linked at ");
- if (nego & (ADVERTISED_1000baseT_Full |
ADVERTISED_1000baseT_Half)) {
debug("1000 ");
duplex = !!(nego & ADVERTISED_1000baseT_Full);
- } else if (nego & (ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half)) {
debug("100 ");
duplex = !!(nego & ADVERTISED_100baseT_Full);
- } else {
debug("10 ");
duplex = !!(nego & ADVERTISED_10baseT_Full);
- }
- if (duplex == DUPLEX_FULL)
debug("full dup ");
- else
debug("half dup ");
- if (duplex == DUPLEX_FULL) {
if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
if (lcladv & ADVERTISE_PAUSE_CAP)
cap = FLOW_CTRL_RX;
else if (rmtadv & LPA_PAUSE_CAP)
cap = FLOW_CTRL_TX;
}
debug("TX Flow ");
if (cap & FLOW_CTRL_TX) {
*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
/* set fct_flow thresholds to 20% and 80% */
*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
& 0x7FUL);
The rest is fine, thanks!

-----Original Message----- From: Marek Vasut [mailto:marex@denx.de] Sent: Tuesday, April 18, 2017 12:47 PM To: u-boot@lists.denx.de Cc: Yuiko Oshino - C18177; Simon Glass; Joe Hershberger Subject: Re: [PATCH] Add support for Microchip LAN75xx and LAN78xx
On 04/18/2017 05:58 PM, Yuiko.Oshino@microchip.com wrote:
[...]
+int lan7x_update_flowcontrol(struct usb_device *udev,
struct ueth_data *dev,
uint32_t *flow, uint32_t *fct_flow) {
- uint32_t lcladv, rmtadv, ctrl1000, stat1000;
- uint32_t advertising = 0, lp_advertising = 0, nego = 0;
- uint32_t duplex = 0;
- u8 cap = 0;
Shouldn't this be split into drivers/net/phy and be part of phylib ? This code looks kinda familiar and similar to what I saw there ...
All I need is to get the auto negotiated duplex mode so that I can set the flow
control.
I am going to use our device specific status registers instead of the standard
registers to get the duplex status to simplify the situation.
OK, CCing Joe, I clearly don't know enough to judge if this is good or not :)
Joe, could you please review this and reply back? I can read only one of our vendor specific registers to get the duplex used. (Although the register offset is not compatible between those two device families). Please let me know what you think. Thank you in advance. Yuiko
- lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
- advertising = lan7x_mii_get_an(lcladv);
- rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
- lp_advertising = lan7x_mii_get_an(rmtadv);
- ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
- stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
- if (ctrl1000 & ADVERTISE_1000HALF)
advertising |= ADVERTISED_1000baseT_Half;
- if (ctrl1000 & ADVERTISE_1000FULL)
advertising |= ADVERTISED_1000baseT_Full;
- if (stat1000 & LPA_1000HALF)
lp_advertising |= ADVERTISED_1000baseT_Half;
- if (stat1000 & LPA_1000FULL)
lp_advertising |= ADVERTISED_1000baseT_Full;
- nego = advertising & lp_advertising;
- debug("LAN7x linked at ");
- if (nego & (ADVERTISED_1000baseT_Full |
ADVERTISED_1000baseT_Half)) {
debug("1000 ");
duplex = !!(nego & ADVERTISED_1000baseT_Full);
- } else if (nego & (ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half)) {
debug("100 ");
duplex = !!(nego & ADVERTISED_100baseT_Full);
- } else {
debug("10 ");
duplex = !!(nego & ADVERTISED_10baseT_Full);
- }
- if (duplex == DUPLEX_FULL)
debug("full dup ");
- else
debug("half dup ");
- if (duplex == DUPLEX_FULL) {
if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
if (lcladv & ADVERTISE_PAUSE_CAP)
cap = FLOW_CTRL_RX;
else if (rmtadv & LPA_PAUSE_CAP)
cap = FLOW_CTRL_TX;
}
debug("TX Flow ");
if (cap & FLOW_CTRL_TX) {
*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
/* set fct_flow thresholds to 20% and 80% */
*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
& 0x7FUL);
The rest is fine, thanks!
-- Best regards, Marek Vasut

On 10 April 2017 at 13:23, Yuiko.Oshino@microchip.com wrote:
From: Yuiko Oshino yuiko.oshino@microchip.com
Add support for Microchip LAN7500, LAN7800 and 7850, USB to 10/100/1000 Ethernet Controllers
Signed-off-by: Yuiko Oshino yuiko.oshino@microchip.com Cc: Marek Vasut marex@denx.de
drivers/usb/Kconfig | 2 + drivers/usb/eth/Kconfig | 18 ++ drivers/usb/eth/Makefile | 2 + drivers/usb/eth/lan75xx.c | 463 ++++++++++++++++++++++++++++++ drivers/usb/eth/lan78xx.c | 611 +++++++++++++++++++++++++++++++++++++++ drivers/usb/eth/lan7x.c | 680 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/eth/lan7x.h | 189 ++++++++++++ drivers/usb/eth/usb_ether.c | 14 + include/usb_ether.h | 12 + 9 files changed, 1991 insertions(+) create mode 100644 drivers/usb/eth/Kconfig create mode 100644 drivers/usb/eth/lan75xx.c create mode 100644 drivers/usb/eth/lan78xx.c create mode 100644 drivers/usb/eth/lan7x.c create mode 100644 drivers/usb/eth/lan7x.h
Please can you remove the non-DM code? New features should use driver model and there is no need to support the old way on new drivers. It encourages people to migrate.
Regards, Simon

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

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

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

On Wed, May 24, 2017 at 10:14 AM, Yuiko.Oshino@microchip.com wrote:
From: Yuiko Oshino yuiko.oshino@microchip.com
-----Original Message----- From: U-Boot [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Yuiko.Oshino@microchip.com Sent: Wednesday, May 10, 2017 11:25 AM To: joe.hershberger@gmail.com Cc: marex@denx.de; u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
From: Yuiko Oshino yuiko.oshino@microchip.com
-----Original Message----- From: Joe Hershberger [mailto:joe.hershberger@gmail.com] Sent: Friday, May 5, 2017 4:59 PM To: Yuiko Oshino - C18177 Cc: u-boot; Marek Vasut Subject: Re: [U-Boot] [PATCH] Add support for Microchip LAN75xx and LAN78xx
Hi Yuiko,
Hi Joe!
[...]
[...]
+/* Loop until the read is completed with timeout */ int +lan7x_wait_for_bit(struct usb_device *udev,
const char *prefix, const u32 index,
const u32 mask, const bool set,
unsigned int timeout_ms)
Can you not use the generic one? include/wait_bit.h
We need to use our own register read function as our device is an USB device. It looks like wait_bit.h does not support function pointer to register read, therefore we need our own.
At least copy the real one.
+{
u32 val;
while (--timeout_ms) {
lan7x_read_reg(udev, index, &val);
if (!set)
val = ~val;
if ((val & mask) == mask)
return 0;
mdelay(1);
}
debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
prefix, index, mask, set);
return -ETIMEDOUT;
+}
+static int lan7x_phy_wait_not_busy(struct usb_device *udev) {
return lan7x_wait_for_bit(udev, __func__,
MII_ACC, MII_ACC_MII_BUSY,
false, 100); }
+int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx) {
u32 val, addr;
/* confirm MII not busy */
if (lan7x_phy_wait_not_busy(udev)) {
debug("MII is busy in %s\n", __func__);
return -ETIMEDOUT;
}
/* set the address, index & direction (read from PHY) */
addr = (phy_id << 11) | (idx << 6) |
MII_ACC_MII_READ | MII_ACC_MII_BUSY;
lan7x_write_reg(udev, MII_ACC, addr);
if (lan7x_phy_wait_not_busy(udev)) {
debug("Timed out reading MII reg %02X\n", idx);
return -ETIMEDOUT;
}
lan7x_read_reg(udev, MII_DATA, &val);
return (u16) (val & 0xFFFF);
+}
+int lan7x_mdio_wait_for_bit(struct usb_device *udev,
const char *prefix,
int phy_id, const u32 index,
const u32 mask, const bool set,
unsigned int timeout_ms) {
u32 val;
while (--timeout_ms) {
val = lan7x_mdio_read(udev, phy_id, index);
if (!set)
val = ~val;
if ((val & mask) == mask)
return 0;
mdelay(1);
}
debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n",
prefix, index, mask, set);
return -ETIMEDOUT;
+}
[...]
+static int lan7x_mii_get_an(uint32_t advertising_reg) {
int advertising = 0;
if (advertising_reg & LPA_LPACK)
advertising |= ADVERTISED_Autoneg;
if (advertising_reg & ADVERTISE_10HALF)
advertising |= ADVERTISED_10baseT_Half;
if (advertising_reg & ADVERTISE_10FULL)
advertising |= ADVERTISED_10baseT_Full;
if (advertising_reg & ADVERTISE_100HALF)
advertising |= ADVERTISED_100baseT_Half;
if (advertising_reg & ADVERTISE_100FULL)
advertising |= ADVERTISED_100baseT_Full;
return advertising;
+}
+int lan7x_update_flowcontrol(struct usb_device *udev,
struct ueth_data *dev,
uint32_t *flow, uint32_t *fct_flow) {
uint32_t lcladv, rmtadv, ctrl1000, stat1000;
uint32_t advertising = 0, lp_advertising = 0, nego = 0;
uint32_t duplex = 0;
u8 cap = 0;
lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE);
advertising = lan7x_mii_get_an(lcladv);
rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA);
lp_advertising = lan7x_mii_get_an(rmtadv);
ctrl1000 = lan7x_mdio_read(udev, dev->phy_id, MII_CTRL1000);
stat1000 = lan7x_mdio_read(udev, dev->phy_id, MII_STAT1000);
if (ctrl1000 & ADVERTISE_1000HALF)
advertising |= ADVERTISED_1000baseT_Half;
if (ctrl1000 & ADVERTISE_1000FULL)
advertising |= ADVERTISED_1000baseT_Full;
if (stat1000 & LPA_1000HALF)
lp_advertising |= ADVERTISED_1000baseT_Half;
if (stat1000 & LPA_1000FULL)
lp_advertising |= ADVERTISED_1000baseT_Full;
nego = advertising & lp_advertising;
debug("LAN7x linked at ");
if (nego & (ADVERTISED_1000baseT_Full |
ADVERTISED_1000baseT_Half)) {
debug("1000 ");
duplex = !!(nego & ADVERTISED_1000baseT_Full);
} else if (nego & (ADVERTISED_100baseT_Full |
ADVERTISED_100baseT_Half)) {
debug("100 ");
duplex = !!(nego & ADVERTISED_100baseT_Full);
} else {
debug("10 ");
duplex = !!(nego & ADVERTISED_10baseT_Full);
}
if (duplex == DUPLEX_FULL)
debug("full dup ");
else
debug("half dup ");
if (duplex == DUPLEX_FULL) {
if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
if (lcladv & ADVERTISE_PAUSE_CAP)
cap = FLOW_CTRL_RX;
else if (rmtadv & LPA_PAUSE_CAP)
cap = FLOW_CTRL_TX;
}
debug("TX Flow ");
if (cap & FLOW_CTRL_TX) {
*flow = (FLOW_CR_TX_FCEN | 0xFFFF);
/* set fct_flow thresholds to 20% and 80% */
*fct_flow = (((MAX_RX_FIFO_SIZE * 2) / (10 * 512))
& 0x7FUL);
*fct_flow <<= 8UL;
*fct_flow |= (((MAX_RX_FIFO_SIZE * 8) / (10 * 512))
& 0x7FUL);
debug("EN ");
} else {
debug("DIS ");
}
debug("RX Flow ");
if (cap & FLOW_CTRL_RX) {
*flow |= FLOW_CR_RX_FCEN;
debug("EN");
} else {
debug("DIS");
}
}
debug("\n");
return 0;
+}
I see where Marek is coming from wrt thisall being in phylib already. I guess you always have a fixed phy internal, so there's no need of the flexibility of phylib. Maybe there's at least opportunity to consolidate subroutines even if not using phylib the normal way.
I am a bit confused what you say. Do you mean that I can keep the current code as is in this area? Please confirm? The access to PHY also needs our own register read/write through USB, so using the phylib is more complicated.
The thought here is to do a minor refactor of the phy.c code such that all this parsing of the bits can be shared code, while access to the MDIO interface is specialized for your driver.
+int lan7x_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss) {
struct usb_interface *iface;
struct usb_interface_descriptor *iface_desc;
int i;
iface = &dev->config.if_desc[ifnum];
iface_desc = &dev->config.if_desc[ifnum].desc;
memset(ss, '\0', sizeof(struct ueth_data));
/* Initialize the ueth_data structure with some useful info */
ss->ifnum = ifnum;
ss->pusb_dev = dev;
ss->subclass = iface_desc->bInterfaceSubClass;
ss->protocol = iface_desc->bInterfaceProtocol;
/*
* We are expecting a minimum of 3 endpoints
* - in, out (bulk), and int.
* We will ignore any others.
*/
for (i = 0; i < iface_desc->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK) {
if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else
ss->ep_out =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
/* is it an interrupt endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT) {
ss->ep_int = iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->irqinterval = iface->ep_desc[i].bInterval;
}
}
debug("Endpoints In %d Out %d Int %d\n",
ss->ep_in, ss->ep_out, ss->ep_int);
/* Do some basic sanity checks, and bail if we find a problem */
if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
!ss->ep_in || !ss->ep_out || !ss->ep_int) {
debug("Problems with device\n");
return 0;
Seems this should return an error.
The caller probe_valid_drivers() in usb_ether.c expects 0 for skipping a bad device.
OK.
}
dev->privptr = (void *)ss;
+#ifndef CONFIG_DM_ETH
/* alloc driver private */
ss->dev_priv = calloc(1, sizeof(struct lan7x_private));
if (!ss->dev_priv)
return 0;
+#endif
return 1;
+}
Thank you. Yuiko _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot
Joe, Please reply so that I can re-submit the patch. Thank you in advance. Best regards, Yuiko

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