diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/usb/eth | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/usb/eth')
-rw-r--r-- | roms/u-boot/drivers/usb/eth/Kconfig | 65 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/Makefile | 14 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/asix.c | 912 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/asix88179.c | 921 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/lan75xx.c | 316 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/lan78xx.c | 479 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/lan7x.c | 500 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/lan7x.h | 237 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/mcs7830.c | 945 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/r8152.c | 1897 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/r8152.h | 683 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/r8152_fw.c | 1194 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/smsc95xx.c | 1080 | ||||
-rw-r--r-- | roms/u-boot/drivers/usb/eth/usb_ether.c | 336 |
14 files changed, 9579 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/usb/eth/Kconfig b/roms/u-boot/drivers/usb/eth/Kconfig new file mode 100644 index 000000000..2f6bfa8e7 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/Kconfig @@ -0,0 +1,65 @@ +menuconfig USB_HOST_ETHER + bool "USB to Ethernet Controller Drivers" + ---help--- + Say Y here if you would like to enable support for USB Ethernet + adapters. + +if USB_HOST_ETHER + +config USB_ETHER_ASIX + bool "ASIX AX8817X (USB 2.0) support" + depends on USB_HOST_ETHER + ---help--- + Say Y here if you would like to support ASIX AX8817X based USB 2.0 + Ethernet Devices. + +config USB_ETHER_ASIX88179 + bool "ASIX AX88179 (USB 3.0) support" + depends on USB_HOST_ETHER + ---help--- + Say Y here if you would like to support ASIX AX88179 based USB 3.0 + Ethernet Devices. + +config USB_ETHER_LAN75XX + bool "Microchip LAN75XX support" + depends on USB_HOST_ETHER + depends on PHYLIB + ---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" + depends on USB_HOST_ETHER + depends on PHYLIB + ---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. + +config USB_ETHER_MCS7830 + bool "MOSCHIP MCS7830 (7730/7830/7832) suppport" + depends on USB_HOST_ETHER + ---help--- + Say Y here if you would like to support MOSCHIP MCS7830 based + (7730/7830/7832) USB 2.0 Ethernet Devices. + +config USB_ETHER_RTL8152 + bool "Realtek RTL8152B/RTL8153 support" + depends on USB_HOST_ETHER + ---help--- + Say Y here if you would like to support Realtek RTL8152B/RTL8153 base + USB Ethernet Devices. This driver also supports compatible devices + from Samsung, Lenovo, TP-LINK and Nvidia. + +config USB_ETHER_SMSC95XX + bool "SMSC LAN95x support" + depends on USB_HOST_ETHER + ---help--- + Say Y here if you would like to support SMSC LAN95xx based USB 2.0 + Ethernet Devices. + +endif diff --git a/roms/u-boot/drivers/usb/eth/Makefile b/roms/u-boot/drivers/usb/eth/Makefile new file mode 100644 index 000000000..2e5d0782e --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2011 The Chromium OS Authors. +# + +# new USB host ethernet layer dependencies +obj-$(CONFIG_USB_HOST_ETHER) += usb_ether.o +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/roms/u-boot/drivers/usb/eth/asix.c b/roms/u-boot/drivers/usb/eth/asix.c new file mode 100644 index 000000000..674f78e21 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/asix.c @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * Patched for AX88772B by Antmicro Ltd <www.antmicro.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <net.h> +#include <usb.h> +#include <malloc.h> +#include <memalign.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include "usb_ether.h" + +/* ASIX AX8817X based USB 2.0 Ethernet Devices */ + +#define AX_CMD_SET_SW_MII 0x06 +#define AX_CMD_READ_MII_REG 0x07 +#define AX_CMD_WRITE_MII_REG 0x08 +#define AX_CMD_SET_HW_MII 0x0a +#define AX_CMD_READ_EEPROM 0x0b +#define AX_CMD_READ_RX_CTL 0x0f +#define AX_CMD_WRITE_RX_CTL 0x10 +#define AX_CMD_WRITE_IPG0 0x12 +#define AX_CMD_READ_NODE_ID 0x13 +#define AX_CMD_WRITE_NODE_ID 0x14 +#define AX_CMD_READ_PHY_ID 0x19 +#define AX_CMD_WRITE_MEDIUM_MODE 0x1b +#define AX_CMD_WRITE_GPIOS 0x1f +#define AX_CMD_SW_RESET 0x20 +#define AX_CMD_SW_PHY_SELECT 0x22 + +#define AX_SWRESET_CLEAR 0x00 +#define AX_SWRESET_PRTE 0x04 +#define AX_SWRESET_PRL 0x08 +#define AX_SWRESET_IPRL 0x20 +#define AX_SWRESET_IPPD 0x40 + +#define AX88772_IPG0_DEFAULT 0x15 +#define AX88772_IPG1_DEFAULT 0x0c +#define AX88772_IPG2_DEFAULT 0x12 + +/* AX88772 & AX88178 Medium Mode Register */ +#define AX_MEDIUM_PF 0x0080 +#define AX_MEDIUM_JFE 0x0040 +#define AX_MEDIUM_TFC 0x0020 +#define AX_MEDIUM_RFC 0x0010 +#define AX_MEDIUM_ENCK 0x0008 +#define AX_MEDIUM_AC 0x0004 +#define AX_MEDIUM_FD 0x0002 +#define AX_MEDIUM_GM 0x0001 +#define AX_MEDIUM_SM 0x1000 +#define AX_MEDIUM_SBP 0x0800 +#define AX_MEDIUM_PS 0x0200 +#define AX_MEDIUM_RE 0x0100 + +#define AX88178_MEDIUM_DEFAULT \ + (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \ + AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \ + AX_MEDIUM_RE) + +#define AX88772_MEDIUM_DEFAULT \ + (AX_MEDIUM_FD | AX_MEDIUM_RFC | \ + AX_MEDIUM_TFC | AX_MEDIUM_PS | \ + AX_MEDIUM_AC | AX_MEDIUM_RE) + +/* AX88772 & AX88178 RX_CTL values */ +#define AX_RX_CTL_SO 0x0080 +#define AX_RX_CTL_AB 0x0008 + +#define AX_DEFAULT_RX_CTL \ + (AX_RX_CTL_SO | AX_RX_CTL_AB) + +/* GPIO 2 toggles */ +#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */ +#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */ +#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */ + +/* local defines */ +#define ASIX_BASE_NAME "asx" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define AX_RX_URB_SIZE 2048 +#define PHY_CONNECT_TIMEOUT 5000 + +/* asix_flags defines */ +#define FLAG_NONE 0 +#define FLAG_TYPE_AX88172 (1U << 0) +#define FLAG_TYPE_AX88772 (1U << 1) +#define FLAG_TYPE_AX88772B (1U << 2) +#define FLAG_EEPROM_MAC (1U << 3) /* initial mac address in eeprom */ + + +/* driver private */ +struct asix_private { + int flags; +#ifdef CONFIG_DM_ETH + struct ueth_data ueth; +#endif +}; + +#ifndef CONFIG_DM_ETH +/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */ +#endif + +/* + * Asix infrastructure commands + */ +static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + + debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x " + "size=%d\n", cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_sndctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + data, + size, + USB_CTRL_SET_TIMEOUT); + + return len == size ? 0 : -1; +} + +static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + + debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_rcvctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + data, + size, + USB_CTRL_GET_TIMEOUT); + return len == size ? 0 : -1; +} + +static inline int asix_set_sw_mii(struct ueth_data *dev) +{ + int ret; + + ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); + if (ret < 0) + debug("Failed to enable software MII access\n"); + return ret; +} + +static inline int asix_set_hw_mii(struct ueth_data *dev) +{ + int ret; + + ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); + if (ret < 0) + debug("Failed to enable hardware MII access\n"); + return ret; +} + +static int asix_mdio_read(struct ueth_data *dev, int phy_id, int loc) +{ + ALLOC_CACHE_ALIGN_BUFFER(__le16, res, 1); + + asix_set_sw_mii(dev); + asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, res); + asix_set_hw_mii(dev); + + debug("asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", + phy_id, loc, le16_to_cpu(*res)); + + return le16_to_cpu(*res); +} + +static void +asix_mdio_write(struct ueth_data *dev, int phy_id, int loc, int val) +{ + ALLOC_CACHE_ALIGN_BUFFER(__le16, res, 1); + *res = cpu_to_le16(val); + + debug("asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", + phy_id, loc, val); + asix_set_sw_mii(dev); + asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, res); + asix_set_hw_mii(dev); +} + +/* + * Asix "high level" commands + */ +static int asix_sw_reset(struct ueth_data *dev, u8 flags) +{ + int ret; + + ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); + if (ret < 0) + debug("Failed to send software reset: %02x\n", ret); + else + udelay(150 * 1000); + + return ret; +} + +static inline int asix_get_phy_addr(struct ueth_data *dev) +{ + ALLOC_CACHE_ALIGN_BUFFER(u8, buf, 2); + + int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); + + debug("asix_get_phy_addr()\n"); + + if (ret < 0) { + debug("Error reading PHYID register: %02x\n", ret); + goto out; + } + debug("asix_get_phy_addr() returning 0x%02x%02x\n", buf[0], buf[1]); + ret = buf[1]; + +out: + return ret; +} + +static int asix_write_medium_mode(struct ueth_data *dev, u16 mode) +{ + int ret; + + debug("asix_write_medium_mode() - mode = 0x%04x\n", mode); + ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, + 0, 0, NULL); + if (ret < 0) { + debug("Failed to write Medium Mode mode to 0x%04x: %02x\n", + mode, ret); + } + return ret; +} + +static u16 asix_read_rx_ctl(struct ueth_data *dev) +{ + ALLOC_CACHE_ALIGN_BUFFER(__le16, v, 1); + + int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, v); + + if (ret < 0) + debug("Error reading RX_CTL register: %02x\n", ret); + else + ret = le16_to_cpu(*v); + return ret; +} + +static int asix_write_rx_ctl(struct ueth_data *dev, u16 mode) +{ + int ret; + + debug("asix_write_rx_ctl() - mode = 0x%04x\n", mode); + ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); + if (ret < 0) { + debug("Failed to write RX_CTL mode to 0x%04x: %02x\n", + mode, ret); + } + return ret; +} + +static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep) +{ + int ret; + + debug("asix_write_gpio() - value = 0x%04x\n", value); + ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); + if (ret < 0) { + debug("Failed to write GPIO value 0x%04x: %02x\n", + value, ret); + } + if (sleep) + udelay(sleep * 1000); + + return ret; +} + +static int asix_write_hwaddr_common(struct ueth_data *dev, uint8_t *enetaddr) +{ + int ret; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); + + memcpy(buf, enetaddr, ETH_ALEN); + + ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, buf); + if (ret < 0) + debug("Failed to set MAC address: %02x\n", ret); + + return ret; +} + +/* + * mii commands + */ + +/* + * mii_nway_restart - restart NWay (autonegotiation) for this interface + * + * Returns 0 on success, negative on error. + */ +static int mii_nway_restart(struct ueth_data *dev) +{ + int bmcr; + int r = -1; + + /* if autoneg is off, it's an error */ + bmcr = asix_mdio_read(dev, dev->phy_id, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + bmcr |= BMCR_ANRESTART; + asix_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr); + r = 0; + } + + return r; +} + +static int asix_read_mac_common(struct ueth_data *dev, + struct asix_private *priv, uint8_t *enetaddr) +{ + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); + int i; + + if (priv->flags & FLAG_EEPROM_MAC) { + for (i = 0; i < (ETH_ALEN >> 1); i++) { + if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, + 0x04 + i, 0, 2, buf) < 0) { + debug("Failed to read SROM address 04h.\n"); + return -1; + } + memcpy(enetaddr + i * 2, buf, 2); + } + } else { + if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf) + < 0) { + debug("Failed to read MAC address.\n"); + return -1; + } + memcpy(enetaddr, buf, ETH_ALEN); + } + + return 0; +} + +static int asix_basic_reset(struct ueth_data *dev) +{ + int embd_phy; + u16 rx_ctl; + + if (asix_write_gpio(dev, + AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5) < 0) + return -1; + + /* 0x10 is the phy id of the embedded 10/100 ethernet phy */ + embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); + if (asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, + embd_phy, 0, 0, NULL) < 0) { + debug("Select PHY #1 failed\n"); + return -1; + } + + if (asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL) < 0) + return -1; + + if (asix_sw_reset(dev, AX_SWRESET_CLEAR) < 0) + return -1; + + if (embd_phy) { + if (asix_sw_reset(dev, AX_SWRESET_IPRL) < 0) + return -1; + } else { + if (asix_sw_reset(dev, AX_SWRESET_PRTE) < 0) + return -1; + } + + rx_ctl = asix_read_rx_ctl(dev); + debug("RX_CTL is 0x%04x after software reset\n", rx_ctl); + if (asix_write_rx_ctl(dev, 0x0000) < 0) + return -1; + + rx_ctl = asix_read_rx_ctl(dev); + debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); + + dev->phy_id = asix_get_phy_addr(dev); + if (dev->phy_id < 0) + debug("Failed to read phy id\n"); + + asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); + asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA); + mii_nway_restart(dev); + + if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0) + return -1; + + if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0, + AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, + AX88772_IPG2_DEFAULT, 0, NULL) < 0) { + debug("Write IPG,IPG1,IPG2 failed\n"); + return -1; + } + + return 0; +} + +static int asix_init_common(struct ueth_data *dev, uint8_t *enetaddr) +{ + int timeout = 0; +#define TIMEOUT_RESOLUTION 50 /* ms */ + int link_detected; + + debug("** %s()\n", __func__); + + if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0) + goto out_err; + + if (asix_write_hwaddr_common(dev, enetaddr) < 0) + goto out_err; + + do { + link_detected = asix_mdio_read(dev, dev->phy_id, MII_BMSR) & + BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + udelay(TIMEOUT_RESOLUTION * 1000); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + if (link_detected) { + if (timeout != 0) + printf("done.\n"); + } else { + printf("unable to connect.\n"); + goto out_err; + } + + /* + * Wait some more to avoid timeout on first transfer + * (e.g. EHCI timed out on TD - token=0x8008d80) + */ + mdelay(25); + + return 0; +out_err: + return -1; +} + +static int asix_send_common(struct ueth_data *dev, void *packet, int length) +{ + int err; + u32 packet_len; + int actual_len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + sizeof(packet_len)); + + debug("** %s(), len %d\n", __func__, length); + + packet_len = (((length) ^ 0x0000ffff) << 16) + (length); + cpu_to_le32s(&packet_len); + + memcpy(msg, &packet_len, sizeof(packet_len)); + memcpy(msg + sizeof(packet_len), (void *)packet, length); + + err = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + (void *)msg, + length + sizeof(packet_len), + &actual_len, + USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %zu, actual = %u, err = %d\n", + length + sizeof(packet_len), actual_len, err); + + return err; +} + +#ifndef CONFIG_DM_ETH +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, struct bd_info *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_init_common(dev, eth->enetaddr); +} + +static int asix_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_send_common(dev, packet, length); +} + +static int asix_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE); + unsigned char *buf_ptr; + int err; + int actual_len; + u32 packet_len; + + debug("** %s()\n", __func__); + + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + AX_RX_URB_SIZE, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE, + actual_len, err); + if (err != 0) { + debug("Rx: failed to receive\n"); + return -1; + } + if (actual_len > AX_RX_URB_SIZE) { + debug("Rx: received too many bytes %d\n", actual_len); + return -1; + } + + buf_ptr = recv_buf; + while (actual_len > 0) { + /* + * 1st 4 bytes contain the length of the actual data as two + * complementary 16-bit words. Extract the length of the data. + */ + if (actual_len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + return -1; + } + memcpy(&packet_len, buf_ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) { + debug("Rx: malformed packet length: %#x (%#x:%#x)\n", + packet_len, (~packet_len >> 16) & 0x7ff, + packet_len & 0x7ff); + return -1; + } + packet_len = packet_len & 0x7ff; + if (packet_len > actual_len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + return -1; + } + + /* Notify net stack */ + net_process_received_packet(buf_ptr + sizeof(packet_len), + packet_len); + + /* Adjust for next iteration. Packets are padded to 16-bits */ + if (packet_len & 1) + packet_len++; + actual_len -= sizeof(packet_len) + packet_len; + buf_ptr += sizeof(packet_len) + packet_len; + } + + return err; +} + +static void asix_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +static int asix_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_write_hwaddr_common(dev, eth->enetaddr); +} + +/* + * Asix probing functions + */ +void asix_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct asix_dongle { + unsigned short vendor; + unsigned short product; + int flags; +}; + +static const struct asix_dongle asix_dongles[] = { + { 0x05ac, 0x1402, FLAG_TYPE_AX88772 }, /* Apple USB Ethernet Adapter */ + { 0x07d1, 0x3c05, FLAG_TYPE_AX88772 }, /* D-Link DUB-E100 H/W Ver B1 */ + { 0x2001, 0x1a02, FLAG_TYPE_AX88772 }, /* D-Link DUB-E100 H/W Ver C1 */ + /* Cables-to-Go USB Ethernet Adapter */ + { 0x0b95, 0x772a, FLAG_TYPE_AX88772 }, + { 0x0b95, 0x7720, FLAG_TYPE_AX88772 }, /* Trendnet TU2-ET100 V3.0R */ + { 0x0b95, 0x1720, FLAG_TYPE_AX88172 }, /* SMC */ + { 0x0db0, 0xa877, FLAG_TYPE_AX88772 }, /* MSI - ASIX 88772a */ + { 0x13b1, 0x0018, FLAG_TYPE_AX88172 }, /* Linksys 200M v2.1 */ + { 0x1557, 0x7720, FLAG_TYPE_AX88772 }, /* 0Q0 cable ethernet */ + /* DLink DUB-E100 H/W Ver B1 Alternate */ + { 0x2001, 0x3c05, FLAG_TYPE_AX88772 }, + /* ASIX 88772B */ + { 0x0b95, 0x772b, FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, + { 0x0b95, 0x7e2b, FLAG_TYPE_AX88772B }, + { 0x0000, 0x0000, FLAG_NONE } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an asix device */ +int asix_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + int ep_in_found = 0, ep_out_found = 0; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; asix_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == asix_dongles[i].vendor && + dev->descriptor.idProduct == asix_dongles[i].product) + /* Found a supported dongle */ + break; + } + + if (asix_dongles[i].vendor == 0) + return 0; + + memset(ss, 0, sizeof(struct ueth_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* 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; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct asix_private)); + if (!ss->dev_priv) + return 0; + + ((struct asix_private *)ss->dev_priv)->flags = asix_dongles[i].flags; + + /* + * 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) { + u8 ep_addr = iface->ep_desc[i].bEndpointAddress; + if (ep_addr & USB_DIR_IN) { + if (!ep_in_found) { + ss->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = 1; + } + } else { + if (!ep_out_found) { + ss->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = 1; + } + } + } + + /* 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; + return 1; +} + +int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + struct asix_private *priv = (struct asix_private *)ss->dev_priv; + + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); + eth->init = asix_init; + eth->send = asix_send; + eth->recv = asix_recv; + eth->halt = asix_halt; + if (!(priv->flags & FLAG_TYPE_AX88172)) + eth->write_hwaddr = asix_write_hwaddr; + eth->priv = ss; + + if (asix_basic_reset(ss)) + return 0; + + /* Get the MAC address */ + if (asix_read_mac_common(ss, priv, eth->enetaddr)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} +#endif + +#ifdef CONFIG_DM_ETH +static int asix_eth_start(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct asix_private *priv = dev_get_priv(dev); + + return asix_init_common(&priv->ueth, pdata->enetaddr); +} + +void asix_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int asix_eth_send(struct udevice *dev, void *packet, int length) +{ + struct asix_private *priv = dev_get_priv(dev); + + return asix_send_common(&priv->ueth, packet, length); +} + +int asix_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + uint8_t *ptr; + int ret, len; + u32 packet_len; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, AX_RX_URB_SIZE); + if (ret == -EAGAIN) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + /* + * 1st 4 bytes contain the length of the actual data as two + * complementary 16-bit words. Extract the length of the data. + */ + if (len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + goto err; + } + memcpy(&packet_len, ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) { + debug("Rx: malformed packet length: %#x (%#x:%#x)\n", + packet_len, (~packet_len >> 16) & 0x7ff, + packet_len & 0x7ff); + goto err; + } + packet_len = packet_len & 0x7ff; + if (packet_len > len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + *packetp = ptr + sizeof(packet_len); + return packet_len; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -EINVAL; +} + +static int asix_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct asix_private *priv = dev_get_priv(dev); + + if (packet_len & 1) + packet_len++; + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int asix_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct asix_private *priv = dev_get_priv(dev); + + if (priv->flags & FLAG_TYPE_AX88172) + return -ENOSYS; + + return asix_write_hwaddr_common(&priv->ueth, pdata->enetaddr); +} + +static int asix_eth_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ss = &priv->ueth; + int ret; + + priv->flags = dev->driver_data; + ret = usb_ether_register(dev, ss, AX_RX_URB_SIZE); + if (ret) + return ret; + + ret = asix_basic_reset(ss); + if (ret) + goto err; + + /* Get the MAC address */ + ret = asix_read_mac_common(ss, priv, pdata->enetaddr); + if (ret) + goto err; + debug("MAC %pM\n", pdata->enetaddr); + + return 0; + +err: + return usb_ether_deregister(ss); +} + +static const struct eth_ops asix_eth_ops = { + .start = asix_eth_start, + .send = asix_eth_send, + .recv = asix_eth_recv, + .free_pkt = asix_free_pkt, + .stop = asix_eth_stop, + .write_hwaddr = asix_write_hwaddr, +}; + +U_BOOT_DRIVER(asix_eth) = { + .name = "asix_eth", + .id = UCLASS_ETH, + .probe = asix_eth_probe, + .ops = &asix_eth_ops, + .priv_auto = sizeof(struct asix_private), + .plat_auto = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id asix_eth_id_table[] = { + /* Apple USB Ethernet Adapter */ + { USB_DEVICE(0x05ac, 0x1402), .driver_info = FLAG_TYPE_AX88772 }, + /* D-Link DUB-E100 H/W Ver B1 */ + { USB_DEVICE(0x07d1, 0x3c05), .driver_info = FLAG_TYPE_AX88772 }, + /* D-Link DUB-E100 H/W Ver C1 */ + { USB_DEVICE(0x2001, 0x1a02), .driver_info = FLAG_TYPE_AX88772 }, + /* Cables-to-Go USB Ethernet Adapter */ + { USB_DEVICE(0x0b95, 0x772a), .driver_info = FLAG_TYPE_AX88772 }, + /* Trendnet TU2-ET100 V3.0R */ + { USB_DEVICE(0x0b95, 0x7720), .driver_info = FLAG_TYPE_AX88772 }, + /* SMC */ + { USB_DEVICE(0x0b95, 0x1720), .driver_info = FLAG_TYPE_AX88172 }, + /* MSI - ASIX 88772a */ + { USB_DEVICE(0x0db0, 0xa877), .driver_info = FLAG_TYPE_AX88772 }, + /* Linksys 200M v2.1 */ + { USB_DEVICE(0x13b1, 0x0018), .driver_info = FLAG_TYPE_AX88172 }, + /* 0Q0 cable ethernet */ + { USB_DEVICE(0x1557, 0x7720), .driver_info = FLAG_TYPE_AX88772 }, + /* DLink DUB-E100 H/W Ver B1 Alternate */ + { USB_DEVICE(0x2001, 0x3c05), .driver_info = FLAG_TYPE_AX88772 }, + /* ASIX 88772B */ + { USB_DEVICE(0x0b95, 0x772b), + .driver_info = FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, + { USB_DEVICE(0x0b95, 0x7e2b), .driver_info = FLAG_TYPE_AX88772B }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(asix_eth, asix_eth_id_table); +#endif diff --git a/roms/u-boot/drivers/usb/eth/asix88179.c b/roms/u-boot/drivers/usb/eth/asix88179.c new file mode 100644 index 000000000..4742a95af --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/asix88179.c @@ -0,0 +1,921 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Rene Griessl <rgriessl@cit-ec.uni-bielefeld.de> + * based on the U-Boot Asix driver as well as information + * from the Linux AX88179_178a driver + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <usb.h> +#include <net.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include <malloc.h> +#include <memalign.h> +#include <errno.h> + +/* ASIX AX88179 based USB 3.0 Ethernet Devices */ +#define AX88179_PHY_ID 0x03 +#define AX_EEPROM_LEN 0x100 +#define AX88179_EEPROM_MAGIC 0x17900b95 +#define AX_MCAST_FLTSIZE 8 +#define AX_MAX_MCAST 64 +#define AX_INT_PPLS_LINK (1 << 16) +#define AX_RXHDR_L4_TYPE_MASK 0x1c +#define AX_RXHDR_L4_TYPE_UDP 4 +#define AX_RXHDR_L4_TYPE_TCP 16 +#define AX_RXHDR_L3CSUM_ERR 2 +#define AX_RXHDR_L4CSUM_ERR 1 +#define AX_RXHDR_CRC_ERR (1 << 29) +#define AX_RXHDR_DROP_ERR (1 << 31) +#define AX_ENDPOINT_INT 0x01 +#define AX_ENDPOINT_IN 0x02 +#define AX_ENDPOINT_OUT 0x03 +#define AX_ACCESS_MAC 0x01 +#define AX_ACCESS_PHY 0x02 +#define AX_ACCESS_EEPROM 0x04 +#define AX_ACCESS_EFUS 0x05 +#define AX_PAUSE_WATERLVL_HIGH 0x54 +#define AX_PAUSE_WATERLVL_LOW 0x55 + +#define PHYSICAL_LINK_STATUS 0x02 + #define AX_USB_SS (1 << 2) + #define AX_USB_HS (1 << 1) + +#define GENERAL_STATUS 0x03 + #define AX_SECLD (1 << 2) + +#define AX_SROM_ADDR 0x07 +#define AX_SROM_CMD 0x0a + #define EEP_RD (1 << 2) + #define EEP_BUSY (1 << 4) + +#define AX_SROM_DATA_LOW 0x08 +#define AX_SROM_DATA_HIGH 0x09 + +#define AX_RX_CTL 0x0b + #define AX_RX_CTL_DROPCRCERR (1 << 8) + #define AX_RX_CTL_IPE (1 << 9) + #define AX_RX_CTL_START (1 << 7) + #define AX_RX_CTL_AP (1 << 5) + #define AX_RX_CTL_AM (1 << 4) + #define AX_RX_CTL_AB (1 << 3) + #define AX_RX_CTL_AMALL (1 << 1) + #define AX_RX_CTL_PRO (1 << 0) + #define AX_RX_CTL_STOP 0 + +#define AX_NODE_ID 0x10 +#define AX_MULFLTARY 0x16 + +#define AX_MEDIUM_STATUS_MODE 0x22 + #define AX_MEDIUM_GIGAMODE (1 << 0) + #define AX_MEDIUM_FULL_DUPLEX (1 << 1) + #define AX_MEDIUM_EN_125MHZ (1 << 3) + #define AX_MEDIUM_RXFLOW_CTRLEN (1 << 4) + #define AX_MEDIUM_TXFLOW_CTRLEN (1 << 5) + #define AX_MEDIUM_RECEIVE_EN (1 << 8) + #define AX_MEDIUM_PS (1 << 9) + #define AX_MEDIUM_JUMBO_EN 0x8040 + +#define AX_MONITOR_MOD 0x24 + #define AX_MONITOR_MODE_RWLC (1 << 1) + #define AX_MONITOR_MODE_RWMP (1 << 2) + #define AX_MONITOR_MODE_PMEPOL (1 << 5) + #define AX_MONITOR_MODE_PMETYPE (1 << 6) + +#define AX_GPIO_CTRL 0x25 + #define AX_GPIO_CTRL_GPIO3EN (1 << 7) + #define AX_GPIO_CTRL_GPIO2EN (1 << 6) + #define AX_GPIO_CTRL_GPIO1EN (1 << 5) + +#define AX_PHYPWR_RSTCTL 0x26 + #define AX_PHYPWR_RSTCTL_BZ (1 << 4) + #define AX_PHYPWR_RSTCTL_IPRL (1 << 5) + #define AX_PHYPWR_RSTCTL_AT (1 << 12) + +#define AX_RX_BULKIN_QCTRL 0x2e +#define AX_CLK_SELECT 0x33 + #define AX_CLK_SELECT_BCS (1 << 0) + #define AX_CLK_SELECT_ACS (1 << 1) + #define AX_CLK_SELECT_ULR (1 << 3) + +#define AX_RXCOE_CTL 0x34 + #define AX_RXCOE_IP (1 << 0) + #define AX_RXCOE_TCP (1 << 1) + #define AX_RXCOE_UDP (1 << 2) + #define AX_RXCOE_TCPV6 (1 << 5) + #define AX_RXCOE_UDPV6 (1 << 6) + +#define AX_TXCOE_CTL 0x35 + #define AX_TXCOE_IP (1 << 0) + #define AX_TXCOE_TCP (1 << 1) + #define AX_TXCOE_UDP (1 << 2) + #define AX_TXCOE_TCPV6 (1 << 5) + #define AX_TXCOE_UDPV6 (1 << 6) + +#define AX_LEDCTRL 0x73 + +#define GMII_PHY_PHYSR 0x11 + #define GMII_PHY_PHYSR_SMASK 0xc000 + #define GMII_PHY_PHYSR_GIGA (1 << 15) + #define GMII_PHY_PHYSR_100 (1 << 14) + #define GMII_PHY_PHYSR_FULL (1 << 13) + #define GMII_PHY_PHYSR_LINK (1 << 10) + +#define GMII_LED_ACT 0x1a + #define GMII_LED_ACTIVE_MASK 0xff8f + #define GMII_LED0_ACTIVE (1 << 4) + #define GMII_LED1_ACTIVE (1 << 5) + #define GMII_LED2_ACTIVE (1 << 6) + +#define GMII_LED_LINK 0x1c + #define GMII_LED_LINK_MASK 0xf888 + #define GMII_LED0_LINK_10 (1 << 0) + #define GMII_LED0_LINK_100 (1 << 1) + #define GMII_LED0_LINK_1000 (1 << 2) + #define GMII_LED1_LINK_10 (1 << 4) + #define GMII_LED1_LINK_100 (1 << 5) + #define GMII_LED1_LINK_1000 (1 << 6) + #define GMII_LED2_LINK_10 (1 << 8) + #define GMII_LED2_LINK_100 (1 << 9) + #define GMII_LED2_LINK_1000 (1 << 10) + #define LED0_ACTIVE (1 << 0) + #define LED0_LINK_10 (1 << 1) + #define LED0_LINK_100 (1 << 2) + #define LED0_LINK_1000 (1 << 3) + #define LED0_FD (1 << 4) + #define LED0_USB3_MASK 0x001f + #define LED1_ACTIVE (1 << 5) + #define LED1_LINK_10 (1 << 6) + #define LED1_LINK_100 (1 << 7) + #define LED1_LINK_1000 (1 << 8) + #define LED1_FD (1 << 9) + #define LED1_USB3_MASK 0x03e0 + #define LED2_ACTIVE (1 << 10) + #define LED2_LINK_1000 (1 << 13) + #define LED2_LINK_100 (1 << 12) + #define LED2_LINK_10 (1 << 11) + #define LED2_FD (1 << 14) + #define LED_VALID (1 << 15) + #define LED2_USB3_MASK 0x7c00 + +#define GMII_PHYPAGE 0x1e +#define GMII_PHY_PAGE_SELECT 0x1f + #define GMII_PHY_PGSEL_EXT 0x0007 + #define GMII_PHY_PGSEL_PAGE0 0x0000 + +/* local defines */ +#define ASIX_BASE_NAME "axg" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define AX_RX_URB_SIZE 1024 * 0x12 +#define BLK_FRAME_SIZE 0x200 +#define PHY_CONNECT_TIMEOUT 5000 + +#define TIMEOUT_RESOLUTION 50 /* ms */ + +#define FLAG_NONE 0 +#define FLAG_TYPE_AX88179 (1U << 0) +#define FLAG_TYPE_AX88178a (1U << 1) +#define FLAG_TYPE_DLINK_DUB1312 (1U << 2) +#define FLAG_TYPE_SITECOM (1U << 3) +#define FLAG_TYPE_SAMSUNG (1U << 4) +#define FLAG_TYPE_LENOVO (1U << 5) +#define FLAG_TYPE_GX3 (1U << 6) + +/* local vars */ +static const struct { + unsigned char ctrl, timer_l, timer_h, size, ifg; +} AX88179_BULKIN_SIZE[] = { + {7, 0x4f, 0, 0x02, 0xff}, + {7, 0x20, 3, 0x03, 0xff}, + {7, 0xae, 7, 0x04, 0xff}, + {7, 0xcc, 0x4c, 0x04, 8}, +}; + +#ifndef CONFIG_DM_ETH +static int curr_eth_dev; /* index for name of next device detected */ +#endif + +/* driver private */ +struct asix_private { +#ifdef CONFIG_DM_ETH + struct ueth_data ueth; + unsigned pkt_cnt; + uint8_t *pkt_data; + uint32_t *pkt_hdr; +#endif + int flags; + int rx_urb_size; + int maxpacketsize; +}; + +/* + * Asix infrastructure commands + */ +static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, size); + + debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + memcpy(buf, data, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_sndctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + buf, + size, + USB_CTRL_SET_TIMEOUT); + + return len == size ? 0 : ECOMM; +} + +static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, size); + + debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_rcvctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + buf, + size, + USB_CTRL_GET_TIMEOUT); + + memcpy(data, buf, size); + + return len == size ? 0 : ECOMM; +} + +static int asix_read_mac(struct ueth_data *dev, uint8_t *enetaddr) +{ + int ret; + + ret = asix_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, 6, 6, enetaddr); + if (ret < 0) + debug("Failed to read MAC address: %02x\n", ret); + + return ret; +} + +static int asix_write_mac(struct ueth_data *dev, uint8_t *enetaddr) +{ + int ret; + + ret = asix_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, enetaddr); + if (ret < 0) + debug("Failed to set MAC address: %02x\n", ret); + + return ret; +} + +static int asix_basic_reset(struct ueth_data *dev, + struct asix_private *dev_priv) +{ + u8 buf[5]; + u16 *tmp16; + u8 *tmp; + + tmp16 = (u16 *)buf; + tmp = (u8 *)buf; + + /* Power up ethernet PHY */ + *tmp16 = 0; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); + + *tmp16 = AX_PHYPWR_RSTCTL_IPRL; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); + mdelay(200); + + *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); + mdelay(200); + + /* RX bulk configuration */ + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + + dev_priv->rx_urb_size = 128 * 20; + + /* Water Level configuration */ + *tmp = 0x34; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); + + *tmp = 0x52; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp); + + /* Enable checksum offload */ + *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | + AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); + + *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | + AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); + + /* Configure RX control register => start operation */ + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); + + *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | + AX_MONITOR_MODE_RWMP; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); + + /* Configure default medium type => giga */ + *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | + AX_MEDIUM_GIGAMODE | AX_MEDIUM_JUMBO_EN; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); + + u16 adv = 0; + adv = ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_LPACK | + ADVERTISE_NPAGE | ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_ADVERTISE, 2, &adv); + + adv = ADVERTISE_1000FULL; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_CTRL1000, 2, &adv); + + return 0; +} + +static int asix_wait_link(struct ueth_data *dev) +{ + int timeout = 0; + int link_detected; + u8 buf[2]; + u16 *tmp16; + + tmp16 = (u16 *)buf; + + do { + asix_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + MII_BMSR, 2, buf); + link_detected = *tmp16 & BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + mdelay(TIMEOUT_RESOLUTION); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + + if (link_detected) { + if (timeout > 0) + printf("done.\n"); + return 0; + } else { + printf("unable to connect.\n"); + return -ENETUNREACH; + } +} + +static int asix_init_common(struct ueth_data *dev, + struct asix_private *dev_priv) +{ + u8 buf[2], tmp[5], link_sts; + u16 *tmp16, mode; + + + tmp16 = (u16 *)buf; + + debug("** %s()\n", __func__); + + /* Configure RX control register => start operation */ + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + if (asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16) != 0) + goto out_err; + + if (asix_wait_link(dev) != 0) { + /*reset device and try again*/ + printf("Reset Ethernet Device\n"); + asix_basic_reset(dev, dev_priv); + if (asix_wait_link(dev) != 0) + goto out_err; + } + + /* Configure link */ + mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN; + + asix_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, + 1, 1, &link_sts); + + asix_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_PHYSR, 2, tmp16); + + if (!(*tmp16 & GMII_PHY_PHYSR_LINK)) { + return 0; + } else if (GMII_PHY_PHYSR_GIGA == (*tmp16 & GMII_PHY_PHYSR_SMASK)) { + mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ | + AX_MEDIUM_JUMBO_EN; + + if (link_sts & AX_USB_SS) + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); + else if (link_sts & AX_USB_HS) + memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5); + else + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } else if (GMII_PHY_PHYSR_100 == (*tmp16 & GMII_PHY_PHYSR_SMASK)) { + mode |= AX_MEDIUM_PS; + + if (link_sts & (AX_USB_SS | AX_USB_HS)) + memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5); + else + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } else { + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } + + /* RX bulk configuration */ + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + + dev_priv->rx_urb_size = (1024 * (tmp[3] + 2)); + if (*tmp16 & GMII_PHY_PHYSR_FULL) + mode |= AX_MEDIUM_FULL_DUPLEX; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &mode); + + return 0; +out_err: + return -1; +} + +static int asix_send_common(struct ueth_data *dev, + struct asix_private *dev_priv, + void *packet, int length) +{ + int err; + u32 packet_len, tx_hdr2; + int actual_len, framesize; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + (2 * sizeof(packet_len))); + + debug("** %s(), len %d\n", __func__, length); + + packet_len = length; + cpu_to_le32s(&packet_len); + + memcpy(msg, &packet_len, sizeof(packet_len)); + framesize = dev_priv->maxpacketsize; + tx_hdr2 = 0; + if (((length + 8) % framesize) == 0) + tx_hdr2 |= 0x80008000; /* Enable padding */ + + cpu_to_le32s(&tx_hdr2); + + memcpy(msg + sizeof(packet_len), &tx_hdr2, sizeof(tx_hdr2)); + + memcpy(msg + sizeof(packet_len) + sizeof(tx_hdr2), + (void *)packet, length); + + err = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + (void *)msg, + length + sizeof(packet_len) + sizeof(tx_hdr2), + &actual_len, + USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %zu, actual = %u, err = %d\n", + length + sizeof(packet_len), actual_len, err); + + return err; +} + +#ifndef CONFIG_DM_ETH +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, struct bd_info *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + return asix_init_common(dev, dev_priv); +} + +static int asix_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_write_mac(dev, eth->enetaddr); +} + +static int asix_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + return asix_send_common(dev, dev_priv, packet, length); +} + +static int asix_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + u16 frame_pos; + int err; + int actual_len; + + int pkt_cnt; + u32 rx_hdr; + u16 hdr_off; + u32 *pkt_hdr; + ALLOC_CACHE_ALIGN_BUFFER(u8, recv_buf, dev_priv->rx_urb_size); + + actual_len = -1; + + debug("** %s()\n", __func__); + + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + dev_priv->rx_urb_size, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", dev_priv->rx_urb_size, + actual_len, err); + + if (err != 0) { + debug("Rx: failed to receive\n"); + return -ECOMM; + } + if (actual_len > dev_priv->rx_urb_size) { + debug("Rx: received too many bytes %d\n", actual_len); + return -EMSGSIZE; + } + + + rx_hdr = *(u32 *)(recv_buf + actual_len - 4); + le32_to_cpus(&rx_hdr); + + pkt_cnt = (u16)rx_hdr; + hdr_off = (u16)(rx_hdr >> 16); + pkt_hdr = (u32 *)(recv_buf + hdr_off); + + + frame_pos = 0; + + while (pkt_cnt--) { + u16 pkt_len; + + le32_to_cpus(pkt_hdr); + pkt_len = (*pkt_hdr >> 16) & 0x1fff; + + frame_pos += 2; + + net_process_received_packet(recv_buf + frame_pos, pkt_len); + + pkt_hdr++; + frame_pos += ((pkt_len + 7) & 0xFFF8)-2; + + if (pkt_cnt == 0) + return 0; + } + return err; +} + +static void asix_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +/* + * Asix probing functions + */ +void ax88179_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct asix_dongle { + unsigned short vendor; + unsigned short product; + int flags; +}; + +static const struct asix_dongle asix_dongles[] = { + { 0x0b95, 0x1790, FLAG_TYPE_AX88179 }, + { 0x0b95, 0x178a, FLAG_TYPE_AX88178a }, + { 0x2001, 0x4a00, FLAG_TYPE_DLINK_DUB1312 }, + { 0x0df6, 0x0072, FLAG_TYPE_SITECOM }, + { 0x04e8, 0xa100, FLAG_TYPE_SAMSUNG }, + { 0x17ef, 0x304b, FLAG_TYPE_LENOVO }, + { 0x04b4, 0x3610, FLAG_TYPE_GX3 }, + { 0x0000, 0x0000, FLAG_NONE } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an asix device */ +int ax88179_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + struct asix_private *dev_priv; + int ep_in_found = 0, ep_out_found = 0; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; asix_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == asix_dongles[i].vendor && + dev->descriptor.idProduct == asix_dongles[i].product) + /* Found a supported dongle */ + break; + } + + if (asix_dongles[i].vendor == 0) + return 0; + + memset(ss, 0, sizeof(struct ueth_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* 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; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct asix_private)); + if (!ss->dev_priv) + return 0; + dev_priv = ss->dev_priv; + dev_priv->flags = asix_dongles[i].flags; + + /* + * 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 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; + continue; + } + + /* is it an BULK endpoint? */ + if (!((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + continue; + + u8 ep_addr = iface->ep_desc[i].bEndpointAddress; + if ((ep_addr & USB_DIR_IN) && !ep_in_found) { + ss->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = 1; + } + if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ss->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + dev_priv->maxpacketsize = + dev->epmaxpacketout[AX_ENDPOINT_OUT]; + ep_out_found = 1; + } + } + 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; + return 1; +} + +int ax88179_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + struct asix_private *dev_priv = (struct asix_private *)ss->dev_priv; + + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); + eth->init = asix_init; + eth->send = asix_send; + eth->recv = asix_recv; + eth->halt = asix_halt; + eth->write_hwaddr = asix_write_hwaddr; + eth->priv = ss; + + if (asix_basic_reset(ss, dev_priv)) + return 0; + + /* Get the MAC address */ + if (asix_read_mac(ss, eth->enetaddr)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} + +#else /* !CONFIG_DM_ETH */ + +static int ax88179_eth_start(struct udevice *dev) +{ + struct asix_private *priv = dev_get_priv(dev); + + return asix_init_common(&priv->ueth, priv); +} + +void ax88179_eth_stop(struct udevice *dev) +{ + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + + debug("** %s()\n", __func__); + + usb_ether_advance_rxbuf(ueth, -1); + priv->pkt_cnt = 0; + priv->pkt_data = NULL; + priv->pkt_hdr = NULL; +} + +int ax88179_eth_send(struct udevice *dev, void *packet, int length) +{ + struct asix_private *priv = dev_get_priv(dev); + + return asix_send_common(&priv->ueth, priv, packet, length); +} + +int ax88179_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + int ret, len; + u16 pkt_len; + + /* No packet left, get a new one */ + if (priv->pkt_cnt == 0) { + uint8_t *ptr; + u16 pkt_cnt; + u16 hdr_off; + u32 rx_hdr; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + + ret = usb_ether_receive(ueth, priv->rx_urb_size); + if (ret < 0) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + if (len < 4) { + usb_ether_advance_rxbuf(ueth, -1); + return -EMSGSIZE; + } + + rx_hdr = *(u32 *)(ptr + len - 4); + le32_to_cpus(&rx_hdr); + + pkt_cnt = (u16)rx_hdr; + if (pkt_cnt == 0) { + usb_ether_advance_rxbuf(ueth, -1); + return 0; + } + + hdr_off = (u16)(rx_hdr >> 16); + if (hdr_off > len - 4) { + usb_ether_advance_rxbuf(ueth, -1); + return -EIO; + } + + priv->pkt_cnt = pkt_cnt; + priv->pkt_data = ptr; + priv->pkt_hdr = (u32 *)(ptr + hdr_off); + debug("%s: %d packets received, pkt header at %d\n", + __func__, (int)priv->pkt_cnt, (int)hdr_off); + } + + le32_to_cpus(priv->pkt_hdr); + pkt_len = (*priv->pkt_hdr >> 16) & 0x1fff; + + *packetp = priv->pkt_data + 2; + + priv->pkt_data += (pkt_len + 7) & 0xFFF8; + priv->pkt_cnt--; + priv->pkt_hdr++; + + debug("%s: return packet of %d bytes (%d packets left)\n", + __func__, (int)pkt_len, priv->pkt_cnt); + return pkt_len; +} + +static int ax88179_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + + if (priv->pkt_cnt == 0) + usb_ether_advance_rxbuf(ueth, -1); + + return 0; +} + +int ax88179_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + + return asix_write_mac(ueth, pdata->enetaddr); +} + +static int ax88179_eth_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct asix_private *priv = dev_get_priv(dev); + struct usb_device *usb_dev; + int ret; + + priv->flags = dev->driver_data; + ret = usb_ether_register(dev, &priv->ueth, AX_RX_URB_SIZE); + if (ret) + return ret; + + usb_dev = priv->ueth.pusb_dev; + priv->maxpacketsize = usb_dev->epmaxpacketout[AX_ENDPOINT_OUT]; + + /* Get the MAC address */ + ret = asix_read_mac(&priv->ueth, pdata->enetaddr); + if (ret) + return ret; + debug("MAC %pM\n", pdata->enetaddr); + + return 0; +} + +static const struct eth_ops ax88179_eth_ops = { + .start = ax88179_eth_start, + .send = ax88179_eth_send, + .recv = ax88179_eth_recv, + .free_pkt = ax88179_free_pkt, + .stop = ax88179_eth_stop, + .write_hwaddr = ax88179_write_hwaddr, +}; + +U_BOOT_DRIVER(ax88179_eth) = { + .name = "ax88179_eth", + .id = UCLASS_ETH, + .probe = ax88179_eth_probe, + .ops = &ax88179_eth_ops, + .priv_auto = sizeof(struct asix_private), + .plat_auto = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id ax88179_eth_id_table[] = { + { USB_DEVICE(0x0b95, 0x1790), .driver_info = FLAG_TYPE_AX88179 }, + { USB_DEVICE(0x0b95, 0x178a), .driver_info = FLAG_TYPE_AX88178a }, + { USB_DEVICE(0x2001, 0x4a00), .driver_info = FLAG_TYPE_DLINK_DUB1312 }, + { USB_DEVICE(0x0df6, 0x0072), .driver_info = FLAG_TYPE_SITECOM }, + { USB_DEVICE(0x04e8, 0xa100), .driver_info = FLAG_TYPE_SAMSUNG }, + { USB_DEVICE(0x17ef, 0x304b), .driver_info = FLAG_TYPE_LENOVO }, + { USB_DEVICE(0x04b4, 0x3610), .driver_info = FLAG_TYPE_GX3 }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(ax88179_eth, ax88179_eth_id_table); +#endif /* !CONFIG_DM_ETH */ diff --git a/roms/u-boot/drivers/usb/eth/lan75xx.c b/roms/u-boot/drivers/usb/eth/lan75xx.c new file mode 100644 index 000000000..4effbc5c8 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/lan75xx.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + */ + +#include <dm.h> +#include <log.h> +#include <usb.h> +#include <linux/bitops.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include "lan7x.h" + +/* LAN75xx specific register/bit defines */ +#define LAN75XX_HW_CFG_BIR BIT(7) + +#define LAN75XX_BURST_CAP 0x034 + +#define LAN75XX_BULK_IN_DLY 0x03C + +#define LAN75XX_RFE_CTL 0x060 + +#define LAN75XX_FCT_RX_CTL 0x090 + +#define LAN75XX_FCT_TX_CTL 0x094 + +#define LAN75XX_FCT_RX_FIFO_END 0x098 + +#define LAN75XX_FCT_TX_FIFO_END 0x09C + +#define LAN75XX_FCT_FLOW 0x0A0 + +/* MAC ADDRESS PERFECT FILTER For LAN75xx */ +#define LAN75XX_ADDR_FILTX 0x300 +#define LAN75XX_ADDR_FILTX_FB_VALID BIT(31) + +/* + * Lan75xx infrastructure commands + */ +static int lan75xx_phy_gig_workaround(struct usb_device *udev, + struct ueth_data *dev) +{ + int ret = 0; + + /* Only internal phy */ + /* Set the phy in Gig loopback */ + lan7x_mdio_write(udev, dev->phy_id, MII_BMCR, + (BMCR_LOOPBACK | BMCR_SPEED1000)); + + /* Wait for the link up */ + ret = lan7x_mdio_wait_for_bit(udev, "BMSR_LSTATUS", + dev->phy_id, MII_BMSR, BMSR_LSTATUS, + true, PHY_CONNECT_TIMEOUT_MS, 1); + if (ret) + return ret; + + /* phy reset */ + return lan7x_pmt_phy_reset(udev, dev); +} + +static int lan75xx_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev) +{ + uint32_t flow = 0, fct_flow = 0; + int ret; + + ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_FCT_FLOW, fct_flow); + if (ret) + return ret; + return lan7x_write_reg(udev, FLOW, flow); +} + +static int lan75xx_set_receive_filter(struct usb_device *udev) +{ + /* No multicast in u-boot */ + return lan7x_write_reg(udev, LAN75XX_RFE_CTL, + RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT); +} + +/* starts the TX path */ +static void lan75xx_start_tx_path(struct usb_device *udev) +{ + /* Enable Tx at MAC */ + lan7x_write_reg(udev, MAC_TX, MAC_TX_TXEN); + + /* Enable Tx at SCSRs */ + lan7x_write_reg(udev, LAN75XX_FCT_TX_CTL, FCT_TX_CTL_EN); +} + +/* Starts the Receive path */ +static void lan75xx_start_rx_path(struct usb_device *udev) +{ + /* Enable Rx at MAC */ + lan7x_write_reg(udev, MAC_RX, + LAN7X_MAC_RX_MAX_SIZE_DEFAULT | + MAC_RX_FCS_STRIP | MAC_RX_RXEN); + + /* Enable Rx at SCSRs */ + lan7x_write_reg(udev, LAN75XX_FCT_RX_CTL, FCT_RX_CTL_EN); +} + +static int lan75xx_basic_reset(struct usb_device *udev, + struct ueth_data *dev, + struct lan7x_private *priv) +{ + int ret; + u32 val; + + ret = lan7x_basic_reset(udev, dev); + if (ret) + return ret; + + /* Keep the chip ID */ + ret = lan7x_read_reg(udev, ID_REV, &val); + if (ret) + return ret; + debug("LAN75xx ID_REV = 0x%08x\n", val); + + priv->chipid = (val & ID_REV_CHIP_ID_MASK) >> 16; + + /* Respond to the IN token with a NAK */ + ret = lan7x_read_reg(udev, HW_CFG, &val); + if (ret) + return ret; + val |= LAN75XX_HW_CFG_BIR; + return lan7x_write_reg(udev, HW_CFG, val); +} + +int lan75xx_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + unsigned char *enetaddr = pdata->enetaddr; + u32 addr_lo = get_unaligned_le32(&enetaddr[0]); + u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]); + int ret; + + /* set hardware address */ + ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX + 4, addr_lo); + if (ret) + return ret; + + addr_hi |= LAN75XX_ADDR_FILTX_FB_VALID; + ret = lan7x_write_reg(udev, LAN75XX_ADDR_FILTX, addr_hi); + if (ret) + return ret; + + debug("MAC addr %pM written\n", enetaddr); + + return 0; +} + +static int lan75xx_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + int ret; + u32 write_buf; + + /* Reset and read Mac addr were done in probe() */ + ret = lan75xx_write_hwaddr(dev); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_BURST_CAP, 0); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN75XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); + if (ret) + return ret; + + /* set FIFO sizes */ + write_buf = (MAX_RX_FIFO_SIZE - 512) / 512; + ret = lan7x_write_reg(udev, LAN75XX_FCT_RX_FIFO_END, write_buf); + if (ret) + return ret; + + write_buf = (MAX_TX_FIFO_SIZE - 512) / 512; + ret = lan7x_write_reg(udev, LAN75XX_FCT_TX_FIFO_END, write_buf); + if (ret) + return ret; + + /* Init Tx */ + ret = lan7x_write_reg(udev, FLOW, 0); + if (ret) + return ret; + + /* Init Rx. Set Vlan, keep default for VLAN on 75xx */ + ret = lan75xx_set_receive_filter(udev); + if (ret) + return ret; + + /* phy workaround for gig link */ + ret = lan75xx_phy_gig_workaround(udev, ueth); + if (ret) + return ret; + + /* Init PHY, autonego, and link */ + ret = lan7x_eth_phylib_connect(dev, &priv->ueth); + if (ret) + return ret; + ret = lan7x_eth_phylib_config_start(dev); + if (ret) + return ret; + + /* + * MAC_CR has to be set after PHY init. + * MAC will auto detect the PHY speed. + */ + ret = lan7x_read_reg(udev, MAC_CR, &write_buf); + if (ret) + return ret; + write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP; + ret = lan7x_write_reg(udev, MAC_CR, write_buf); + if (ret) + return ret; + + lan75xx_start_tx_path(udev); + lan75xx_start_rx_path(udev); + + return lan75xx_update_flowcontrol(udev, ueth); +} + +int lan75xx_read_rom_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + int ret; + + /* + * Refer to the doc/README.enetaddr and doc/README.usb for + * the U-Boot MAC address policy + */ + ret = lan7x_read_eeprom_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_plat(dev); + int ret; + + /* Do a reset in order to get the MAC address from HW */ + if (lan75xx_basic_reset(udev, ueth, priv)) + return 0; + + /* Get the MAC address */ + /* + * We must set the eth->enetaddr from HW because the upper layer + * will force to use the environmental var (usbethaddr) or random if + * there is no valid MAC address in eth->enetaddr. + * + * Refer to the doc/README.enetaddr and doc/README.usb for + * the U-Boot MAC address policy + */ + lan7x_read_eeprom_mac(pdata->enetaddr, udev); + /* Do not return 0 for not finding MAC addr in HW */ + + ret = usb_ether_register(dev, ueth, RX_URB_SIZE); + if (ret) + return ret; + + /* Register phylib */ + return lan7x_phylib_register(dev); +} + +static const struct eth_ops lan75xx_eth_ops = { + .start = lan75xx_eth_start, + .send = lan7x_eth_send, + .recv = lan7x_eth_recv, + .free_pkt = lan7x_free_pkt, + .stop = lan7x_eth_stop, + .write_hwaddr = lan75xx_write_hwaddr, + .read_rom_hwaddr = lan75xx_read_rom_hwaddr, +}; + +U_BOOT_DRIVER(lan75xx_eth) = { + .name = "lan75xx_eth", + .id = UCLASS_ETH, + .probe = lan75xx_eth_probe, + .remove = lan7x_eth_remove, + .ops = &lan75xx_eth_ops, + .priv_auto = sizeof(struct lan7x_private), + .plat_auto = 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); diff --git a/roms/u-boot/drivers/usb/eth/lan78xx.c b/roms/u-boot/drivers/usb/eth/lan78xx.c new file mode 100644 index 000000000..37912a1d0 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/lan78xx.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + */ + +#include <dm.h> +#include <log.h> +#include <net.h> +#include <usb.h> +#include <linux/bitops.h> +#include "usb_ether.h" +#include "lan7x.h" + +/* LAN78xx specific register/bit defines */ +#define LAN78XX_HW_CFG_LED1_EN BIT(21) /* Muxed with EEDO */ +#define LAN78XX_HW_CFG_LED0_EN BIT(20) /* Muxed with EECLK */ + +#define LAN78XX_USB_CFG0 0x080 +#define LAN78XX_USB_CFG0_BIR BIT(6) + +#define LAN78XX_BURST_CAP 0x090 + +#define LAN78XX_BULK_IN_DLY 0x094 + +#define LAN78XX_RFE_CTL 0x0B0 + +#define LAN78XX_FCT_RX_CTL 0x0C0 + +#define LAN78XX_FCT_TX_CTL 0x0C4 + +#define LAN78XX_FCT_RX_FIFO_END 0x0C8 + +#define LAN78XX_FCT_TX_FIFO_END 0x0CC + +#define LAN78XX_FCT_FLOW 0x0D0 + +#define LAN78XX_MAF_BASE 0x400 +#define LAN78XX_MAF_HIX 0x00 +#define LAN78XX_MAF_LOX 0x04 +#define LAN78XX_MAF_HI_BEGIN (LAN78XX_MAF_BASE + LAN78XX_MAF_HIX) +#define LAN78XX_MAF_LO_BEGIN (LAN78XX_MAF_BASE + LAN78XX_MAF_LOX) +#define LAN78XX_MAF_HI(index) (LAN78XX_MAF_BASE + (8 * (index)) + \ + LAN78XX_MAF_HIX) +#define LAN78XX_MAF_LO(index) (LAN78XX_MAF_BASE + (8 * (index)) + \ + LAN78XX_MAF_LOX) +#define LAN78XX_MAF_HI_VALID BIT(31) + +/* OTP registers */ +#define LAN78XX_OTP_BASE_ADDR 0x00001000 + +#define LAN78XX_OTP_PWR_DN (LAN78XX_OTP_BASE_ADDR + 4 * 0x00) +#define LAN78XX_OTP_PWR_DN_PWRDN_N BIT(0) + +#define LAN78XX_OTP_ADDR1 (LAN78XX_OTP_BASE_ADDR + 4 * 0x01) +#define LAN78XX_OTP_ADDR1_15_11 0x1F + +#define LAN78XX_OTP_ADDR2 (LAN78XX_OTP_BASE_ADDR + 4 * 0x02) +#define LAN78XX_OTP_ADDR2_10_3 0xFF + +#define LAN78XX_OTP_RD_DATA (LAN78XX_OTP_BASE_ADDR + 4 * 0x06) + +#define LAN78XX_OTP_FUNC_CMD (LAN78XX_OTP_BASE_ADDR + 4 * 0x08) +#define LAN78XX_OTP_FUNC_CMD_READ BIT(0) + +#define LAN78XX_OTP_CMD_GO (LAN78XX_OTP_BASE_ADDR + 4 * 0x0A) +#define LAN78XX_OTP_CMD_GO_GO BIT(0) + +#define LAN78XX_OTP_STATUS (LAN78XX_OTP_BASE_ADDR + 4 * 0x0C) +#define LAN78XX_OTP_STATUS_BUSY BIT(0) + +#define LAN78XX_OTP_INDICATOR_1 0xF3 +#define LAN78XX_OTP_INDICATOR_2 0xF7 + +/* + * Lan78xx infrastructure commands + */ +static int lan78xx_read_raw_otp(struct usb_device *udev, u32 offset, + u32 length, u8 *data) +{ + int i; + int ret; + u32 buf; + + ret = lan7x_read_reg(udev, LAN78XX_OTP_PWR_DN, &buf); + if (ret) + return ret; + + if (buf & LAN78XX_OTP_PWR_DN_PWRDN_N) { + /* clear it and wait to be cleared */ + ret = lan7x_write_reg(udev, LAN78XX_OTP_PWR_DN, 0); + if (ret) + return ret; + + ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_PWR_DN_PWRDN_N", + LAN78XX_OTP_PWR_DN, + LAN78XX_OTP_PWR_DN_PWRDN_N, + false, 1000, 0); + if (ret) + return ret; + } + + for (i = 0; i < length; i++) { + ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR1, + ((offset + i) >> 8) & + LAN78XX_OTP_ADDR1_15_11); + if (ret) + return ret; + ret = lan7x_write_reg(udev, LAN78XX_OTP_ADDR2, + ((offset + i) & LAN78XX_OTP_ADDR2_10_3)); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_OTP_FUNC_CMD, + LAN78XX_OTP_FUNC_CMD_READ); + if (ret) + return ret; + ret = lan7x_write_reg(udev, LAN78XX_OTP_CMD_GO, + LAN78XX_OTP_CMD_GO_GO); + + if (ret) + return ret; + + ret = lan7x_wait_for_bit(udev, "LAN78XX_OTP_STATUS_BUSY", + LAN78XX_OTP_STATUS, + LAN78XX_OTP_STATUS_BUSY, + false, 1000, 0); + if (ret) + return ret; + + ret = lan7x_read_reg(udev, LAN78XX_OTP_RD_DATA, &buf); + if (ret) + return ret; + + data[i] = (u8)(buf & 0xFF); + } + + return 0; +} + +static int lan78xx_read_otp(struct usb_device *udev, u32 offset, + u32 length, u8 *data) +{ + u8 sig; + int ret; + + ret = lan78xx_read_raw_otp(udev, 0, 1, &sig); + + if (!ret) { + if (sig == LAN78XX_OTP_INDICATOR_1) + offset = offset; + else if (sig == LAN78XX_OTP_INDICATOR_2) + offset += 0x100; + else + return -EINVAL; + ret = lan78xx_read_raw_otp(udev, offset, length, data); + if (ret) + return ret; + } + debug("LAN78x: MAC address from OTP = %pM\n", data); + + return ret; +} + +static int lan78xx_read_otp_mac(unsigned char *enetaddr, + struct usb_device *udev) +{ + int ret; + + memset(enetaddr, 0, 6); + + ret = lan78xx_read_otp(udev, + EEPROM_MAC_OFFSET, + ETH_ALEN, + enetaddr); + if (!ret && is_valid_ethaddr(enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from OTP %pM\n", enetaddr); + return 0; + } + debug("MAC address read from OTP invalid %pM\n", enetaddr); + + memset(enetaddr, 0, 6); + return -EINVAL; +} + +static int lan78xx_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev) +{ + uint32_t flow = 0, fct_flow = 0; + int ret; + + ret = lan7x_update_flowcontrol(udev, dev, &flow, &fct_flow); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_FCT_FLOW, fct_flow); + if (ret) + return ret; + return lan7x_write_reg(udev, FLOW, flow); +} + +static int lan78xx_read_mac(unsigned char *enetaddr, + struct usb_device *udev, + struct lan7x_private *priv) +{ + u32 val; + int ret; + int saved = 0, done = 0; + + /* + * Depends on chip, some EEPROM pins are muxed with LED function. + * disable & restore LED function to access EEPROM. + */ + if ((priv->chipid == ID_REV_CHIP_ID_7800) || + (priv->chipid == ID_REV_CHIP_ID_7850)) { + ret = lan7x_read_reg(udev, HW_CFG, &val); + if (ret) + return ret; + saved = val; + val &= ~(LAN78XX_HW_CFG_LED1_EN | LAN78XX_HW_CFG_LED0_EN); + ret = lan7x_write_reg(udev, HW_CFG, val); + if (ret) + goto restore; + } + + /* + * Refer to the doc/README.enetaddr and doc/README.usb for + * the U-Boot MAC address policy + */ + /* try reading mac address from EEPROM, then from OTP */ + ret = lan7x_read_eeprom_mac(enetaddr, udev); + if (!ret) + done = 1; + +restore: + if ((priv->chipid == ID_REV_CHIP_ID_7800) || + (priv->chipid == ID_REV_CHIP_ID_7850)) { + ret = lan7x_write_reg(udev, HW_CFG, saved); + if (ret) + return ret; + } + /* if the EEPROM mac address is good, then exit */ + if (done) + return 0; + + /* try reading mac address from OTP if the device is LAN78xx */ + return lan78xx_read_otp_mac(enetaddr, udev); +} + +static int lan78xx_set_receive_filter(struct usb_device *udev) +{ + /* No multicast in u-boot for now */ + return lan7x_write_reg(udev, LAN78XX_RFE_CTL, + RFE_CTL_BCAST_EN | RFE_CTL_DA_PERFECT); +} + +/* starts the TX path */ +static void lan78xx_start_tx_path(struct usb_device *udev) +{ + /* Enable Tx at MAC */ + lan7x_write_reg(udev, MAC_TX, MAC_TX_TXEN); + + /* Enable Tx at SCSRs */ + lan7x_write_reg(udev, LAN78XX_FCT_TX_CTL, FCT_TX_CTL_EN); +} + +/* Starts the Receive path */ +static void lan78xx_start_rx_path(struct usb_device *udev) +{ + /* Enable Rx at MAC */ + lan7x_write_reg(udev, MAC_RX, + LAN7X_MAC_RX_MAX_SIZE_DEFAULT | + MAC_RX_FCS_STRIP | MAC_RX_RXEN); + + /* Enable Rx at SCSRs */ + lan7x_write_reg(udev, LAN78XX_FCT_RX_CTL, FCT_RX_CTL_EN); +} + +static int lan78xx_basic_reset(struct usb_device *udev, + struct ueth_data *dev, + struct lan7x_private *priv) +{ + int ret; + u32 val; + + ret = lan7x_basic_reset(udev, dev); + if (ret) + return ret; + + /* Keep the chip ID */ + ret = lan7x_read_reg(udev, ID_REV, &val); + if (ret) + return ret; + debug("LAN78xx ID_REV = 0x%08x\n", val); + + priv->chipid = (val & ID_REV_CHIP_ID_MASK) >> 16; + + /* Respond to the IN token with a NAK */ + ret = lan7x_read_reg(udev, LAN78XX_USB_CFG0, &val); + if (ret) + return ret; + val &= ~LAN78XX_USB_CFG0_BIR; + return lan7x_write_reg(udev, LAN78XX_USB_CFG0, val); +} + +int lan78xx_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + unsigned char *enetaddr = pdata->enetaddr; + u32 addr_lo = get_unaligned_le32(&enetaddr[0]); + u32 addr_hi = (u32)get_unaligned_le16(&enetaddr[4]); + int ret; + + /* set hardware address */ + ret = lan7x_write_reg(udev, RX_ADDRL, addr_lo); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, RX_ADDRH, addr_hi); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_MAF_LO(0), addr_lo); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_MAF_HI(0), + addr_hi | LAN78XX_MAF_HI_VALID); + if (ret) + return ret; + + debug("MAC addr %pM written\n", enetaddr); + + return 0; +} + +static int lan78xx_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct lan7x_private *priv = dev_get_priv(dev); + + int ret; + u32 write_buf; + + /* Reset and read Mac addr were done in probe() */ + ret = lan78xx_write_hwaddr(dev); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_BURST_CAP, 0); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, INT_STS, 0xFFFFFFFF); + if (ret) + return ret; + + /* set FIFO sizes */ + ret = lan7x_write_reg(udev, LAN78XX_FCT_RX_FIFO_END, + (MAX_RX_FIFO_SIZE - 512) / 512); + if (ret) + return ret; + + ret = lan7x_write_reg(udev, LAN78XX_FCT_TX_FIFO_END, + (MAX_TX_FIFO_SIZE - 512) / 512); + if (ret) + return ret; + + /* Init Tx */ + ret = lan7x_write_reg(udev, FLOW, 0); + if (ret) + return ret; + + /* Init Rx. Set Vlan, keep default for VLAN on 78xx */ + ret = lan78xx_set_receive_filter(udev); + if (ret) + return ret; + + /* Init PHY, autonego, and link */ + ret = lan7x_eth_phylib_connect(dev, &priv->ueth); + if (ret) + return ret; + ret = lan7x_eth_phylib_config_start(dev); + if (ret) + return ret; + + /* + * MAC_CR has to be set after PHY init. + * MAC will auto detect the PHY speed. + */ + ret = lan7x_read_reg(udev, MAC_CR, &write_buf); + if (ret) + return ret; + write_buf |= MAC_CR_AUTO_DUPLEX | MAC_CR_AUTO_SPEED | MAC_CR_ADP; + ret = lan7x_write_reg(udev, MAC_CR, write_buf); + if (ret) + return ret; + + lan78xx_start_tx_path(udev); + lan78xx_start_rx_path(udev); + + return lan78xx_update_flowcontrol(udev, &priv->ueth); +} + +int lan78xx_read_rom_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(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_plat(dev); + int ret; + + /* Do a reset in order to get the MAC address from HW */ + if (lan78xx_basic_reset(udev, ueth, priv)) + return 0; + + /* Get the MAC address */ + /* + * We must set the eth->enetaddr from HW because the upper layer + * will force to use the environmental var (usbethaddr) or random if + * there is no valid MAC address in eth->enetaddr. + */ + lan78xx_read_mac(pdata->enetaddr, udev, priv); + /* Do not return 0 for not finding MAC addr in HW */ + + ret = usb_ether_register(dev, ueth, RX_URB_SIZE); + if (ret) + return ret; + + /* Register phylib */ + return lan7x_phylib_register(dev); +} + +static const struct eth_ops lan78xx_eth_ops = { + .start = lan78xx_eth_start, + .send = lan7x_eth_send, + .recv = lan7x_eth_recv, + .free_pkt = lan7x_free_pkt, + .stop = lan7x_eth_stop, + .write_hwaddr = lan78xx_write_hwaddr, + .read_rom_hwaddr = lan78xx_read_rom_hwaddr, +}; + +U_BOOT_DRIVER(lan78xx_eth) = { + .name = "lan78xx_eth", + .id = UCLASS_ETH, + .probe = lan78xx_eth_probe, + .remove = lan7x_eth_remove, + .ops = &lan78xx_eth_ops, + .priv_auto = sizeof(struct lan7x_private), + .plat_auto = 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); diff --git a/roms/u-boot/drivers/usb/eth/lan7x.c b/roms/u-boot/drivers/usb/eth/lan7x.c new file mode 100644 index 000000000..0a283619a --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/lan7x.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + */ + +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <miiphy.h> +#include <memalign.h> +#include <net.h> +#include <usb.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include "lan7x.h" + +/* + * Lan7x infrastructure commands + */ +int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); + + cpu_to_le32s(&data); + tmpbuf[0] = data; + + len = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, tmpbuf, sizeof(data), + USB_CTRL_SET_TIMEOUT_MS); + if (len != sizeof(data)) { + debug("%s failed: index=%d, data=%d, len=%d", + __func__, index, data, len); + return -EIO; + } + return 0; +} + +int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1); + + len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, tmpbuf, sizeof(*data), + USB_CTRL_GET_TIMEOUT_MS); + *data = tmpbuf[0]; + if (len != sizeof(*data)) { + debug("%s failed: index=%d, len=%d", __func__, index, len); + return -EIO; + } + + le32_to_cpus(data); + return 0; +} + +static int lan7x_phy_wait_not_busy(struct usb_device *udev) +{ + return lan7x_wait_for_bit(udev, __func__, + MII_ACC, MII_ACC_MII_BUSY, + false, 100, 0); +} + +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx) +{ + u32 val, addr; + + /* confirm MII not busy */ + if (lan7x_phy_wait_not_busy(udev)) { + debug("MII is busy in %s\n", __func__); + return -ETIMEDOUT; + } + + /* set the address, index & direction (read from PHY) */ + addr = (phy_id << 11) | (idx << 6) | + MII_ACC_MII_READ | MII_ACC_MII_BUSY; + lan7x_write_reg(udev, MII_ACC, addr); + + if (lan7x_phy_wait_not_busy(udev)) { + debug("Timed out reading MII reg %02X\n", idx); + return -ETIMEDOUT; + } + + lan7x_read_reg(udev, MII_DATA, &val); + + return val & 0xFFFF; +} + +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, int regval) +{ + u32 addr; + + /* confirm MII not busy */ + if (lan7x_phy_wait_not_busy(udev)) { + debug("MII is busy in %s\n", __func__); + return; + } + + lan7x_write_reg(udev, MII_DATA, regval); + + /* set the address, index & direction (write to PHY) */ + addr = (phy_id << 11) | (idx << 6) | + MII_ACC_MII_WRITE | MII_ACC_MII_BUSY; + lan7x_write_reg(udev, MII_ACC, addr); + + if (lan7x_phy_wait_not_busy(udev)) + debug("Timed out writing MII reg %02X\n", idx); +} + +/* + * Lan7x phylib wrappers + */ +static int lan7x_phylib_mdio_read(struct mii_dev *bus, + int addr, int devad, int reg) +{ + struct usb_device *udev = dev_get_parent_priv(bus->priv); + + return lan7x_mdio_read(udev, addr, reg); +} + +static int lan7x_phylib_mdio_write(struct mii_dev *bus, + int addr, int devad, int reg, u16 val) +{ + struct usb_device *udev = dev_get_parent_priv(bus->priv); + + lan7x_mdio_write(udev, addr, reg, (int)val); + + return 0; +} + +/* + * Lan7x eeprom functions + */ +static int lan7x_eeprom_confirm_not_busy(struct usb_device *udev) +{ + return lan7x_wait_for_bit(udev, __func__, + E2P_CMD, E2P_CMD_EPC_BUSY, + false, 100, 0); +} + +static int lan7x_wait_eeprom(struct usb_device *udev) +{ + return lan7x_wait_for_bit(udev, __func__, + E2P_CMD, + (E2P_CMD_EPC_BUSY | E2P_CMD_EPC_TIMEOUT), + false, 100, 0); +} + +static int lan7x_read_eeprom(struct usb_device *udev, + u32 offset, u32 length, u8 *data) +{ + u32 val; + int i, ret; + + ret = lan7x_eeprom_confirm_not_busy(udev); + if (ret) + return ret; + + for (i = 0; i < length; i++) { + val = E2P_CMD_EPC_BUSY | E2P_CMD_EPC_CMD_READ | + (offset & E2P_CMD_EPC_ADDR_MASK); + lan7x_write_reg(udev, E2P_CMD, val); + + ret = lan7x_wait_eeprom(udev); + if (ret) + return ret; + + lan7x_read_reg(udev, E2P_DATA, &val); + data[i] = val & 0xFF; + offset++; + } + return ret; +} + +/* + * Lan7x phylib functions + */ +int lan7x_phylib_register(struct udevice *udev) +{ + struct usb_device *usbdev = dev_get_parent_priv(udev); + struct lan7x_private *priv = dev_get_priv(udev); + int ret; + + priv->mdiobus = mdio_alloc(); + if (!priv->mdiobus) { + printf("mdio_alloc failed\n"); + return -ENOMEM; + } + priv->mdiobus->read = lan7x_phylib_mdio_read; + priv->mdiobus->write = lan7x_phylib_mdio_write; + sprintf(priv->mdiobus->name, + "lan7x_mdiobus-d%hu-p%hu", usbdev->devnum, usbdev->portnr); + priv->mdiobus->priv = (void *)udev; + + ret = mdio_register(priv->mdiobus); + if (ret) { + printf("mdio_register failed\n"); + free(priv->mdiobus); + return -ENOMEM; + } + + return 0; +} + +int lan7x_eth_phylib_connect(struct udevice *udev, struct ueth_data *dev) +{ + struct lan7x_private *priv = dev_get_priv(udev); + + priv->phydev = phy_connect(priv->mdiobus, dev->phy_id, + udev, PHY_INTERFACE_MODE_MII); + + if (!priv->phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + return 0; +} + +int lan7x_eth_phylib_config_start(struct udevice *udev) +{ + struct lan7x_private *priv = dev_get_priv(udev); + int ret; + + /* configure supported modes */ + priv->phydev->supported = PHY_BASIC_FEATURES | + SUPPORTED_1000baseT_Full | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause; + + priv->phydev->advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full | + ADVERTISED_Pause | + ADVERTISED_Asym_Pause | + ADVERTISED_Autoneg; + + priv->phydev->autoneg = AUTONEG_ENABLE; + + ret = genphy_config_aneg(priv->phydev); + if (ret) { + printf("genphy_config_aneg failed\n"); + return ret; + } + ret = phy_startup(priv->phydev); + if (ret) { + printf("phy_startup failed\n"); + return ret; + } + + debug("** %s() speed %i duplex %i adv %X supp %X\n", __func__, + priv->phydev->speed, priv->phydev->duplex, + priv->phydev->advertising, priv->phydev->supported); + + return 0; +} + +int lan7x_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev, + uint32_t *flow, uint32_t *fct_flow) +{ + uint32_t lcladv, rmtadv; + u8 cap = 0; + struct lan7x_private *priv = dev_get_priv(udev->dev); + + debug("** %s()\n", __func__); + debug("** %s() priv->phydev->speed %i duplex %i\n", __func__, + priv->phydev->speed, priv->phydev->duplex); + + if (priv->phydev->duplex == DUPLEX_FULL) { + lcladv = lan7x_mdio_read(udev, dev->phy_id, MII_ADVERTISE); + rmtadv = lan7x_mdio_read(udev, dev->phy_id, MII_LPA); + cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + + debug("TX Flow "); + if (cap & FLOW_CTRL_TX) { + *flow = (FLOW_CR_TX_FCEN | 0xFFFF); + /* set fct_flow thresholds to 20% and 80% */ + *fct_flow = ((MAX_RX_FIFO_SIZE * 2) / (10 * 512)) + & 0x7FUL; + *fct_flow <<= 8UL; + *fct_flow |= ((MAX_RX_FIFO_SIZE * 8) / (10 * 512)) + & 0x7FUL; + debug("EN "); + } else { + debug("DIS "); + } + debug("RX Flow "); + if (cap & FLOW_CTRL_RX) { + *flow |= FLOW_CR_RX_FCEN; + debug("EN"); + } else { + debug("DIS"); + } + } + debug("\n"); + return 0; +} + +int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev) +{ + int ret; + + memset(enetaddr, 0, 6); + + ret = lan7x_read_eeprom(udev, 0, 1, enetaddr); + + if ((ret == 0) && (enetaddr[0] == EEPROM_INDICATOR)) { + ret = lan7x_read_eeprom(udev, + EEPROM_MAC_OFFSET, ETH_ALEN, + enetaddr); + if ((ret == 0) && is_valid_ethaddr(enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from EEPROM %pM\n", + enetaddr); + return 0; + } + } + debug("MAC address read from EEPROM invalid %pM\n", enetaddr); + + memset(enetaddr, 0, 6); + return -EINVAL; +} + +int lan7x_pmt_phy_reset(struct usb_device *udev, + struct ueth_data *dev) +{ + int ret; + u32 data; + + ret = lan7x_read_reg(udev, PMT_CTL, &data); + if (ret) + return ret; + ret = lan7x_write_reg(udev, PMT_CTL, data | PMT_CTL_PHY_RST); + if (ret) + return ret; + + /* for LAN7x, we need to check PMT_CTL_READY asserted */ + ret = lan7x_wait_for_bit(udev, "PMT_CTL_PHY_RST", + PMT_CTL, PMT_CTL_PHY_RST, + false, 1000, 0); /* could take over 125mS */ + if (ret) + return ret; + + return lan7x_wait_for_bit(udev, "PMT_CTL_READY", + PMT_CTL, PMT_CTL_READY, + true, 1000, 0); +} + +int lan7x_basic_reset(struct usb_device *udev, + struct ueth_data *dev) +{ + int ret; + + dev->phy_id = LAN7X_INTERNAL_PHY_ID; /* fixed phy id */ + + ret = lan7x_write_reg(udev, HW_CFG, HW_CFG_LRST); + if (ret) + return ret; + + ret = lan7x_wait_for_bit(udev, "HW_CFG_LRST", + HW_CFG, HW_CFG_LRST, + false, 1000, 0); + if (ret) + return ret; + + debug("USB devnum %d portnr %d\n", udev->devnum, udev->portnr); + + return lan7x_pmt_phy_reset(udev, dev); +} + +void lan7x_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int lan7x_eth_send(struct udevice *dev, void *packet, int length) +{ + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + int err; + int actual_len; + u32 tx_cmd_a; + u32 tx_cmd_b; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)); + + debug("** %s(), len %d, buf %#x\n", __func__, length, + (unsigned int)(ulong) msg); + if (length > PKTSIZE) + return -ENOSPC; + + /* LAN7x disable all TX offload features for u-boot */ + tx_cmd_a = (u32) (length & TX_CMD_A_LEN_MASK) | TX_CMD_A_FCS; + tx_cmd_b = 0; + cpu_to_le32s(&tx_cmd_a); + cpu_to_le32s(&tx_cmd_b); + + /* prepend cmd_a and cmd_b */ + memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a)); + memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b)); + memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet, + length); + err = usb_bulk_msg(ueth->pusb_dev, + usb_sndbulkpipe(ueth->pusb_dev, ueth->ep_out), + (void *)msg, + length + sizeof(tx_cmd_a) + + sizeof(tx_cmd_b), + &actual_len, USB_BULK_SEND_TIMEOUT_MS); + debug("Tx: len = %u, actual = %u, err = %d\n", + (unsigned int)(length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)), + (unsigned int)actual_len, err); + + return err; +} + +int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct lan7x_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + uint8_t *ptr; + int ret, len; + u32 packet_len = 0; + u32 rx_cmd_a = 0; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, RX_URB_SIZE); + if (ret == -EAGAIN) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + /* + * 1st 4 bytes contain the length of the actual data plus error info. + * Extract data length. + */ + if (len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + goto err; + } + memcpy(&rx_cmd_a, 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); + goto err; + } + packet_len = (u16) (rx_cmd_a & RX_CMD_A_LEN_MASK); + + if (packet_len > len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + /* + * For LAN7x, the length in command A does not + * include command A, B, and C length. + * So use it as is. + */ + + *packetp = ptr + 10; + return packet_len; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -EINVAL; +} + +int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct lan7x_private *priv = dev_get_priv(dev); + + packet_len = ALIGN(packet_len, 4); + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int lan7x_eth_remove(struct udevice *dev) +{ + struct lan7x_private *priv = dev_get_priv(dev); + + debug("** %s()\n", __func__); + free(priv->phydev); + mdio_unregister(priv->mdiobus); + mdio_free(priv->mdiobus); + + return 0; +} diff --git a/roms/u-boot/drivers/usb/eth/lan7x.h b/roms/u-boot/drivers/usb/eth/lan7x.h new file mode 100644 index 000000000..f71e8c726 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/lan7x.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2017 Microchip Technology Inc. All rights reserved. + */ + +#include <console.h> +#include <time.h> +#include <watchdog.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/errno.h> + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 +#define USB_VENDOR_REQUEST_GET_STATS 0xA2 + +/* Tx Command A */ +#define TX_CMD_A_FCS BIT(22) +#define TX_CMD_A_LEN_MASK 0x000FFFFF + +/* Rx Command A */ +#define RX_CMD_A_RXE BIT(18) +#define RX_CMD_A_LEN_MASK 0x00003FFF + +/* SCSRs */ +#define ID_REV 0x00 +#define ID_REV_CHIP_ID_MASK 0xFFFF0000 +#define ID_REV_CHIP_ID_7500 0x7500 +#define ID_REV_CHIP_ID_7800 0x7800 +#define ID_REV_CHIP_ID_7850 0x7850 + +#define INT_STS 0x0C + +#define HW_CFG 0x010 +#define HW_CFG_LRST BIT(1) + +#define PMT_CTL 0x014 +#define PMT_CTL_PHY_PWRUP BIT(10) +#define PMT_CTL_READY BIT(7) +#define PMT_CTL_PHY_RST BIT(4) + +#define E2P_CMD 0x040 +#define E2P_CMD_EPC_BUSY BIT(31) +#define E2P_CMD_EPC_CMD_READ 0x00000000 +#define E2P_CMD_EPC_TIMEOUT BIT(10) +#define E2P_CMD_EPC_ADDR_MASK 0x000001FF + +#define E2P_DATA 0x044 + +#define RFE_CTL_BCAST_EN BIT(10) +#define RFE_CTL_DA_PERFECT BIT(1) + +#define FCT_RX_CTL_EN BIT(31) + +#define FCT_TX_CTL_EN BIT(31) + +#define MAC_CR 0x100 +#define MAC_CR_ADP BIT(13) +#define MAC_CR_AUTO_DUPLEX BIT(12) +#define MAC_CR_AUTO_SPEED BIT(11) + +#define MAC_RX 0x104 +#define MAC_RX_FCS_STRIP BIT(4) +#define MAC_RX_RXEN BIT(0) + +#define MAC_TX 0x108 +#define MAC_TX_TXEN BIT(0) + +#define FLOW 0x10C +#define FLOW_CR_TX_FCEN BIT(30) +#define FLOW_CR_RX_FCEN BIT(29) + +#define RX_ADDRH 0x118 +#define RX_ADDRL 0x11C + +#define MII_ACC 0x120 +#define MII_ACC_MII_READ 0x00000000 +#define MII_ACC_MII_WRITE 0x00000002 +#define MII_ACC_MII_BUSY BIT(0) + +#define MII_DATA 0x124 + +#define SS_USB_PKT_SIZE 1024 +#define HS_USB_PKT_SIZE 512 +#define FS_USB_PKT_SIZE 64 + +#define MAX_RX_FIFO_SIZE (12 * 1024) +#define MAX_TX_FIFO_SIZE (12 * 1024) +#define DEFAULT_BULK_IN_DELAY 0x0800 + +#define EEPROM_INDICATOR 0xA5 +#define EEPROM_MAC_OFFSET 0x01 + +/* Some extra defines */ +#define LAN7X_INTERNAL_PHY_ID 1 + +#define LAN7X_MAC_RX_MAX_SIZE(mtu) \ + ((mtu) << 16) /* Max frame size */ +#define LAN7X_MAC_RX_MAX_SIZE_DEFAULT \ + LAN7X_MAC_RX_MAX_SIZE(PKTSIZE_ALIGN + 4 /* VLAN */ + 4 /* CRC */) + +/* Timeouts */ +#define USB_CTRL_SET_TIMEOUT_MS 5000 +#define USB_CTRL_GET_TIMEOUT_MS 5000 +#define USB_BULK_SEND_TIMEOUT_MS 5000 +#define USB_BULK_RECV_TIMEOUT_MS 5000 +#define TIMEOUT_RESOLUTION_MS 50 +#define PHY_CONNECT_TIMEOUT_MS 5000 + +#define RX_URB_SIZE 2048 + +/* driver private */ +struct lan7x_private { + struct ueth_data ueth; + u32 chipid; /* Chip or device ID */ + struct mii_dev *mdiobus; + struct phy_device *phydev; +}; + +/* + * Lan7x infrastructure commands + */ + +int lan7x_write_reg(struct usb_device *udev, u32 index, u32 data); + +int lan7x_read_reg(struct usb_device *udev, u32 index, u32 *data); + +/* + * FIXME: Code should not be in header files. Nive this to a file common to + * the two drivers. + */ +static inline int lan7x_wait_for_bit(struct usb_device *udev, + const char *prefix, const u32 reg, + const u32 mask, const bool set, + const unsigned int timeout_ms, + const bool breakable) +{ + u32 val; + unsigned long start = get_timer(0); + + while (1) { + lan7x_read_reg(udev, reg, &val); + + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + if (get_timer(start) > timeout_ms) + break; + + if (breakable && ctrlc()) { + puts("Abort\n"); + return -EINTR; + } + + udelay(1); + WATCHDOG_RESET(); + } + + debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n", prefix, reg, + mask, set); + + return -ETIMEDOUT; +} + +int lan7x_mdio_read(struct usb_device *udev, int phy_id, int idx); + +void lan7x_mdio_write(struct usb_device *udev, int phy_id, int idx, + int regval); + +static inline int lan7x_mdio_wait_for_bit(struct usb_device *udev, + const char *prefix, + int phy_id, const u32 reg, + const u32 mask, const bool set, + const unsigned int timeout_ms, + const bool breakable) +{ + u32 val; + unsigned long start = get_timer(0); + + while (1) { + val = lan7x_mdio_read(udev, phy_id, reg); + + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + if (get_timer(start) > timeout_ms) + break; + + if (breakable && ctrlc()) { + puts("Abort\n"); + return -EINTR; + } + + udelay(1); + WATCHDOG_RESET(); + } + + debug("%s: Timeout (reg=0x%x mask=%08x wait_set=%i)\n", prefix, reg, + mask, set); + + return -ETIMEDOUT; +} + +int lan7x_phylib_register(struct udevice *udev); + +int lan7x_eth_phylib_connect(struct udevice *udev, struct ueth_data *dev); + +int lan7x_eth_phylib_config_start(struct udevice *udev); + +int lan7x_pmt_phy_reset(struct usb_device *udev, + struct ueth_data *dev); + +int lan7x_update_flowcontrol(struct usb_device *udev, + struct ueth_data *dev, + uint32_t *flow, uint32_t *fct_flow); + +int lan7x_read_eeprom_mac(unsigned char *enetaddr, struct usb_device *udev); + +int lan7x_basic_reset(struct usb_device *udev, + struct ueth_data *dev); + +void lan7x_eth_stop(struct udevice *dev); + +int lan7x_eth_send(struct udevice *dev, void *packet, int length); + +int lan7x_eth_recv(struct udevice *dev, int flags, uchar **packetp); + +int lan7x_free_pkt(struct udevice *dev, uchar *packet, int packet_len); + +int lan7x_eth_remove(struct udevice *dev); diff --git a/roms/u-boot/drivers/usb/eth/mcs7830.c b/roms/u-boot/drivers/usb/eth/mcs7830.c new file mode 100644 index 000000000..783ab62f6 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/mcs7830.c @@ -0,0 +1,945 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Gerhard Sittig <gsi@denx.de> + * based on the U-Boot Asix driver as well as information + * from the Linux Moschip driver + */ + +/* + * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <net.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include <malloc.h> +#include <memalign.h> +#include <usb.h> + +#include "usb_ether.h" + +#define MCS7830_BASE_NAME "mcs" + +#define USBCALL_TIMEOUT 1000 +#define LINKSTATUS_TIMEOUT 5000 /* link status, connect timeout */ +#define LINKSTATUS_TIMEOUT_RES 50 /* link status, resolution in msec */ + +#define MCS7830_RX_URB_SIZE 2048 + +/* command opcodes */ +#define MCS7830_WR_BREQ 0x0d +#define MCS7830_RD_BREQ 0x0e + +/* register layout, numerical offset specs for USB API calls */ +struct mcs7830_regs { + uint8_t multicast_hashes[8]; + uint8_t packet_gap[2]; + uint8_t phy_data[2]; + uint8_t phy_command[2]; + uint8_t configuration; + uint8_t ether_address[6]; + uint8_t frame_drop_count; + uint8_t pause_threshold; +}; +#define REG_MULTICAST_HASH offsetof(struct mcs7830_regs, multicast_hashes) +#define REG_PHY_DATA offsetof(struct mcs7830_regs, phy_data) +#define REG_PHY_CMD offsetof(struct mcs7830_regs, phy_command) +#define REG_CONFIG offsetof(struct mcs7830_regs, configuration) +#define REG_ETHER_ADDR offsetof(struct mcs7830_regs, ether_address) +#define REG_FRAME_DROP_COUNTER offsetof(struct mcs7830_regs, frame_drop_count) +#define REG_PAUSE_THRESHOLD offsetof(struct mcs7830_regs, pause_threshold) + +/* bit masks and default values for the above registers */ +#define PHY_CMD1_READ 0x40 +#define PHY_CMD1_WRITE 0x20 +#define PHY_CMD1_PHYADDR 0x01 + +#define PHY_CMD2_PEND 0x80 +#define PHY_CMD2_READY 0x40 + +#define CONF_CFG 0x80 +#define CONF_SPEED100 0x40 +#define CONF_FDX_ENABLE 0x20 +#define CONF_RXENABLE 0x10 +#define CONF_TXENABLE 0x08 +#define CONF_SLEEPMODE 0x04 +#define CONF_ALLMULTICAST 0x02 +#define CONF_PROMISCUOUS 0x01 + +#define PAUSE_THRESHOLD_DEFAULT 0 + +/* bit masks for the status byte which follows received ethernet frames */ +#define STAT_RX_FRAME_CORRECT 0x20 +#define STAT_RX_LARGE_FRAME 0x10 +#define STAT_RX_CRC_ERROR 0x08 +#define STAT_RX_ALIGNMENT_ERROR 0x04 +#define STAT_RX_LENGTH_ERROR 0x02 +#define STAT_RX_SHORT_FRAME 0x01 + +/* + * struct mcs7830_private - private driver data for an individual adapter + * @config: shadow for the network adapter's configuration register + * @mchash: shadow for the network adapter's multicast hash registers + */ +struct mcs7830_private { +#ifdef CONFIG_DM_ETH + uint8_t rx_buf[MCS7830_RX_URB_SIZE]; + struct ueth_data ueth; +#endif + uint8_t config; + uint8_t mchash[8]; +}; + +/* + * mcs7830_read_reg() - read a register of the network adapter + * @udev: network device to read from + * @idx: index of the register to start reading from + * @size: number of bytes to read + * @data: buffer to read into + * Return: zero upon success, negative upon error + */ +static int mcs7830_read_reg(struct usb_device *udev, uint8_t idx, + uint16_t size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size); + + debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size); + + len = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + MCS7830_RD_BREQ, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, idx, buf, size, + USBCALL_TIMEOUT); + if (len != size) { + debug("%s() len=%d != sz=%d\n", __func__, len, size); + return -EIO; + } + memcpy(data, buf, size); + return 0; +} + +/* + * mcs7830_write_reg() - write a register of the network adapter + * @udev: network device to write to + * @idx: index of the register to start writing to + * @size: number of bytes to write + * @data: buffer holding the data to write + * Return: zero upon success, negative upon error + */ +static int mcs7830_write_reg(struct usb_device *udev, uint8_t idx, + uint16_t size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, size); + + debug("%s() idx=0x%04X sz=%d\n", __func__, idx, size); + + memcpy(buf, data, size); + len = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + MCS7830_WR_BREQ, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, idx, buf, size, + USBCALL_TIMEOUT); + if (len != size) { + debug("%s() len=%d != sz=%d\n", __func__, len, size); + return -EIO; + } + return 0; +} + +/* + * mcs7830_phy_emit_wait() - emit PHY read/write access, wait for its execution + * @udev: network device to talk to + * @rwflag: PHY_CMD1_READ or PHY_CMD1_WRITE opcode + * @index: number of the PHY register to read or write + * Return: zero upon success, negative upon error + */ +static int mcs7830_phy_emit_wait(struct usb_device *udev, + uint8_t rwflag, uint8_t index) +{ + int rc; + int retry; + uint8_t cmd[2]; + + /* send the PHY read/write request */ + cmd[0] = rwflag | PHY_CMD1_PHYADDR; + cmd[1] = PHY_CMD2_PEND | (index & 0x1f); + rc = mcs7830_write_reg(udev, REG_PHY_CMD, sizeof(cmd), cmd); + if (rc < 0) + return rc; + + /* wait for the response to become available (usually < 1ms) */ + retry = 10; + do { + rc = mcs7830_read_reg(udev, REG_PHY_CMD, sizeof(cmd), cmd); + if (rc < 0) + return rc; + if (cmd[1] & PHY_CMD2_READY) + return 0; + if (!retry--) + return -ETIMEDOUT; + mdelay(1); + } while (1); + /* UNREACH */ +} + +/* + * mcs7830_read_phy() - read a PHY register of the network adapter + * @udev: network device to read from + * @index: index of the PHY register to read from + * Return: non-negative 16bit register content, negative upon error + */ +static int mcs7830_read_phy(struct usb_device *udev, uint8_t index) +{ + int rc; + uint16_t val; + + /* issue the PHY read request and wait for its execution */ + rc = mcs7830_phy_emit_wait(udev, PHY_CMD1_READ, index); + if (rc < 0) + return rc; + + /* fetch the PHY data which was read */ + rc = mcs7830_read_reg(udev, REG_PHY_DATA, sizeof(val), &val); + if (rc < 0) + return rc; + rc = le16_to_cpu(val); + debug("%s(%d) => 0x%04X\n", __func__, index, rc); + return rc; +} + +/* + * mcs7830_write_phy() - write a PHY register of the network adapter + * @udev: network device to write to + * @index: index of the PHY register to write to + * @val: value to write to the PHY register + * Return: zero upon success, negative upon error + */ +static int mcs7830_write_phy(struct usb_device *udev, uint8_t index, + uint16_t val) +{ + int rc; + + debug("%s(%d, 0x%04X)\n", __func__, index, val); + + /* setup the PHY data which is to get written */ + val = cpu_to_le16(val); + rc = mcs7830_write_reg(udev, REG_PHY_DATA, sizeof(val), &val); + if (rc < 0) + return rc; + + /* issue the PHY write request and wait for its execution */ + rc = mcs7830_phy_emit_wait(udev, PHY_CMD1_WRITE, index); + if (rc < 0) + return rc; + + return 0; +} + +/* + * mcs7830_write_config() - write to the network adapter's config register + * @udev: network device to write to + * @priv: private data + * Return: zero upon success, negative upon error + * + * the data which gets written is taken from the shadow config register + * within the device driver's private data + */ +static int mcs7830_write_config(struct usb_device *udev, + struct mcs7830_private *priv) +{ + int rc; + + debug("%s()\n", __func__); + + rc = mcs7830_write_reg(udev, REG_CONFIG, + sizeof(priv->config), &priv->config); + if (rc < 0) { + debug("writing config to adapter failed\n"); + return rc; + } + + return 0; +} + +/* + * mcs7830_write_mchash() - write the network adapter's multicast filter + * @udev: network device to write to + * @priv: private data + * Return: zero upon success, negative upon error + * + * the data which gets written is taken from the shadow multicast hashes + * within the device driver's private data + */ +static int mcs7830_write_mchash(struct usb_device *udev, + struct mcs7830_private *priv) +{ + int rc; + + debug("%s()\n", __func__); + + rc = mcs7830_write_reg(udev, REG_MULTICAST_HASH, + sizeof(priv->mchash), &priv->mchash); + if (rc < 0) { + debug("writing multicast hash to adapter failed\n"); + return rc; + } + + return 0; +} + +/* + * mcs7830_set_autoneg() - setup and trigger ethernet link autonegotiation + * @udev: network device to run link negotiation on + * Return: zero upon success, negative upon error + * + * the routine advertises available media and starts autonegotiation + */ +static int mcs7830_set_autoneg(struct usb_device *udev) +{ + int adv, flg; + int rc; + + debug("%s()\n", __func__); + + /* + * algorithm taken from the Linux driver, which took it from + * "the original mcs7830 version 1.4 driver": + * + * enable all media, reset BMCR, enable auto neg, restart + * auto neg while keeping the enable auto neg flag set + */ + + adv = ADVERTISE_PAUSE_CAP | ADVERTISE_ALL | ADVERTISE_CSMA; + rc = mcs7830_write_phy(udev, MII_ADVERTISE, adv); + + flg = 0; + if (!rc) + rc = mcs7830_write_phy(udev, MII_BMCR, flg); + + flg |= BMCR_ANENABLE; + if (!rc) + rc = mcs7830_write_phy(udev, MII_BMCR, flg); + + flg |= BMCR_ANRESTART; + if (!rc) + rc = mcs7830_write_phy(udev, MII_BMCR, flg); + + return rc; +} + +/* + * mcs7830_get_rev() - identify a network adapter's chip revision + * @udev: network device to identify + * Return: non-negative number, reflecting the revision number + * + * currently, only "rev C and higher" and "below rev C" are needed, so + * the return value is #1 for "below rev C", and #2 for "rev C and above" + */ +static int mcs7830_get_rev(struct usb_device *udev) +{ + uint8_t buf[2]; + int rc; + int rev; + + /* register 22 is readable in rev C and higher */ + rc = mcs7830_read_reg(udev, REG_FRAME_DROP_COUNTER, sizeof(buf), buf); + if (rc < 0) + rev = 1; + else + rev = 2; + debug("%s() rc=%d, rev=%d\n", __func__, rc, rev); + return rev; +} + +/* + * mcs7830_apply_fixup() - identify an adapter and potentially apply fixups + * @udev: network device to identify and apply fixups to + * Return: zero upon success (no errors emitted from here) + * + * this routine identifies the network adapter's chip revision, and applies + * fixups for known issues + */ +static int mcs7830_apply_fixup(struct usb_device *udev) +{ + int rev; + int i; + uint8_t thr; + + rev = mcs7830_get_rev(udev); + debug("%s() rev=%d\n", __func__, rev); + + /* + * rev C requires setting the pause threshold (the Linux driver + * is inconsistent, the implementation does it for "rev C + * exactly", the introductory comment says "rev C and above") + */ + if (rev == 2) { + debug("%s: applying rev C fixup\n", __func__); + thr = PAUSE_THRESHOLD_DEFAULT; + for (i = 0; i < 2; i++) { + (void)mcs7830_write_reg(udev, REG_PAUSE_THRESHOLD, + sizeof(thr), &thr); + mdelay(1); + } + } + + return 0; +} + +/* + * mcs7830_basic_reset() - bring the network adapter into a known first state + * @eth: network device to act upon + * Return: zero upon success, negative upon error + * + * this routine initializes the network adapter such that subsequent invocations + * of the interface callbacks can exchange ethernet frames; link negotiation is + * triggered from here already and continues in background + */ +static int mcs7830_basic_reset(struct usb_device *udev, + struct mcs7830_private *priv) +{ + int rc; + + debug("%s()\n", __func__); + + /* + * comment from the respective Linux driver, which + * unconditionally sets the ALLMULTICAST flag as well: + * should not be needed, but does not work otherwise + */ + priv->config = CONF_TXENABLE; + priv->config |= CONF_ALLMULTICAST; + + rc = mcs7830_set_autoneg(udev); + if (rc < 0) { + pr_err("setting autoneg failed\n"); + return rc; + } + + rc = mcs7830_write_mchash(udev, priv); + if (rc < 0) { + pr_err("failed to set multicast hash\n"); + return rc; + } + + rc = mcs7830_write_config(udev, priv); + if (rc < 0) { + pr_err("failed to set configuration\n"); + return rc; + } + + rc = mcs7830_apply_fixup(udev); + if (rc < 0) { + pr_err("fixup application failed\n"); + return rc; + } + + return 0; +} + +/* + * mcs7830_read_mac() - read an ethernet adapter's MAC address + * @udev: network device to read from + * @enetaddr: place to put ethernet MAC address + * Return: zero upon success, negative upon error + * + * this routine fetches the MAC address stored within the ethernet adapter, + * and stores it in the ethernet interface's data structure + */ +static int mcs7830_read_mac(struct usb_device *udev, unsigned char enetaddr[]) +{ + int rc; + uint8_t buf[ETH_ALEN]; + + debug("%s()\n", __func__); + + rc = mcs7830_read_reg(udev, REG_ETHER_ADDR, ETH_ALEN, buf); + if (rc < 0) { + debug("reading MAC from adapter failed\n"); + return rc; + } + + memcpy(enetaddr, buf, ETH_ALEN); + return 0; +} + +static int mcs7830_write_mac_common(struct usb_device *udev, + unsigned char enetaddr[]) +{ + int rc; + + debug("%s()\n", __func__); + + rc = mcs7830_write_reg(udev, REG_ETHER_ADDR, ETH_ALEN, enetaddr); + if (rc < 0) { + debug("writing MAC to adapter failed\n"); + return rc; + } + return 0; +} + +static int mcs7830_init_common(struct usb_device *udev) +{ + int timeout; + int have_link; + + debug("%s()\n", __func__); + + timeout = 0; + do { + have_link = mcs7830_read_phy(udev, MII_BMSR) & BMSR_LSTATUS; + if (have_link) + break; + udelay(LINKSTATUS_TIMEOUT_RES * 1000); + timeout += LINKSTATUS_TIMEOUT_RES; + } while (timeout < LINKSTATUS_TIMEOUT); + if (!have_link) { + debug("ethernet link is down\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int mcs7830_send_common(struct ueth_data *ueth, void *packet, + int length) +{ + struct usb_device *udev = ueth->pusb_dev; + int rc; + int gotlen; + /* there is a status byte after the ethernet frame */ + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, PKTSIZE + sizeof(uint8_t)); + + memcpy(buf, packet, length); + rc = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, ueth->ep_out), + &buf[0], length, &gotlen, + USBCALL_TIMEOUT); + debug("%s() TX want len %d, got len %d, rc %d\n", + __func__, length, gotlen, rc); + return rc; +} + +static int mcs7830_recv_common(struct ueth_data *ueth, uint8_t *buf) +{ + int rc, wantlen, gotlen; + uint8_t sts; + + debug("%s()\n", __func__); + + /* fetch input data from the adapter */ + wantlen = MCS7830_RX_URB_SIZE; + rc = usb_bulk_msg(ueth->pusb_dev, + usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), + &buf[0], wantlen, &gotlen, + USBCALL_TIMEOUT); + debug("%s() RX want len %d, got len %d, rc %d\n", + __func__, wantlen, gotlen, rc); + if (rc != 0) { + pr_err("RX: failed to receive\n"); + return rc; + } + if (gotlen > wantlen) { + pr_err("RX: got too many bytes (%d)\n", gotlen); + return -EIO; + } + + /* + * the bulk message that we received from USB contains exactly + * one ethernet frame and a trailing status byte + */ + if (gotlen < sizeof(sts)) + return -EIO; + gotlen -= sizeof(sts); + sts = buf[gotlen]; + + if (sts == STAT_RX_FRAME_CORRECT) { + debug("%s() got a frame, len=%d\n", __func__, gotlen); + return gotlen; + } + + debug("RX: frame error (sts 0x%02X, %s %s %s %s %s)\n", + sts, + (sts & STAT_RX_LARGE_FRAME) ? "large" : "-", + (sts & STAT_RX_LENGTH_ERROR) ? "length" : "-", + (sts & STAT_RX_SHORT_FRAME) ? "short" : "-", + (sts & STAT_RX_CRC_ERROR) ? "crc" : "-", + (sts & STAT_RX_ALIGNMENT_ERROR) ? "align" : "-"); + return -EIO; +} + +#ifndef CONFIG_DM_ETH +/* + * mcs7830_init() - network interface's init callback + * @udev: network device to initialize + * @bd: board information + * Return: zero upon success, negative upon error + * + * after initial setup during probe() and get_info(), this init() callback + * ensures that the link is up and subsequent send() and recv() calls can + * exchange ethernet frames + */ +static int mcs7830_init(struct eth_device *eth, struct bd_info *bd) +{ + struct ueth_data *dev = eth->priv; + + return mcs7830_init_common(dev->pusb_dev); +} + +/* + * mcs7830_send() - network interface's send callback + * @eth: network device to send the frame from + * @packet: ethernet frame content + * @length: ethernet frame length + * Return: zero upon success, negative upon error + * + * this routine send an ethernet frame out of the network interface + */ +static int mcs7830_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = eth->priv; + + return mcs7830_send_common(dev, packet, length); +} + +/* + * mcs7830_recv() - network interface's recv callback + * @eth: network device to receive frames from + * Return: zero upon success, negative upon error + * + * this routine checks for available ethernet frames that the network + * interface might have received, and notifies the network stack + */ +static int mcs7830_recv(struct eth_device *eth) +{ + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, buf, MCS7830_RX_URB_SIZE); + struct ueth_data *ueth = eth->priv; + int len; + + len = mcs7830_recv_common(ueth, buf); + if (len >= 0) { + net_process_received_packet(buf, len); + return 0; + } + + return len; +} + +/* + * mcs7830_halt() - network interface's halt callback + * @eth: network device to cease operation of + * Return: none + * + * this routine is supposed to undo the effect of previous initialization and + * ethernet frames exchange; in this implementation it's a NOP + */ +static void mcs7830_halt(struct eth_device *eth) +{ + debug("%s()\n", __func__); +} + +/* + * mcs7830_write_mac() - write an ethernet adapter's MAC address + * @eth: network device to write to + * Return: zero upon success, negative upon error + * + * this routine takes the MAC address from the ethernet interface's data + * structure, and writes it into the ethernet adapter such that subsequent + * exchange of ethernet frames uses this address + */ +static int mcs7830_write_mac(struct eth_device *eth) +{ + struct ueth_data *ueth = eth->priv; + + return mcs7830_write_mac_common(ueth->pusb_dev, eth->enetaddr); +} + +/* + * mcs7830_iface_idx - index of detected network interfaces + * + * this counter keeps track of identified supported interfaces, + * to assign unique names as more interfaces are found + */ +static int mcs7830_iface_idx; + +/* + * mcs7830_eth_before_probe() - network driver's before_probe callback + * Return: none + * + * this routine initializes driver's internal data in preparation of + * subsequent probe callbacks + */ +void mcs7830_eth_before_probe(void) +{ + mcs7830_iface_idx = 0; +} + +/* + * struct mcs7830_dongle - description of a supported Moschip ethernet dongle + * @vendor: 16bit USB vendor identification + * @product: 16bit USB product identification + * + * this structure describes a supported USB ethernet dongle by means of the + * vendor and product codes found during USB enumeration; no flags are held + * here since all supported dongles have identical behaviour, and required + * fixups get determined at runtime, such that no manual configuration is + * needed + */ +struct mcs7830_dongle { + uint16_t vendor; + uint16_t product; +}; + +/* + * mcs7830_dongles - the list of supported Moschip based USB ethernet dongles + */ +static const struct mcs7830_dongle mcs7830_dongles[] = { + { 0x9710, 0x7832, }, /* Moschip 7832 */ + { 0x9710, 0x7830, }, /* Moschip 7830 */ + { 0x9710, 0x7730, }, /* Moschip 7730 */ + { 0x0df6, 0x0021, }, /* Sitecom LN 30 */ +}; + +/* + * mcs7830_eth_probe() - network driver's probe callback + * @dev: detected USB device to check + * @ifnum: detected USB interface to check + * @ss: USB ethernet data structure to fill in upon match + * Return: #1 upon match, #0 upon mismatch or error + * + * this routine checks whether the found USB device is supported by + * this ethernet driver, and upon match fills in the USB ethernet + * data structure which later is passed to the get_info callback + */ +int mcs7830_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; + struct mcs7830_private *priv; + int ep_in_found, ep_out_found, ep_intr_found; + + debug("%s()\n", __func__); + + /* iterate the list of supported dongles */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &iface->desc; + for (i = 0; i < ARRAY_SIZE(mcs7830_dongles); i++) { + if (dev->descriptor.idVendor == mcs7830_dongles[i].vendor && + dev->descriptor.idProduct == mcs7830_dongles[i].product) + break; + } + if (i == ARRAY_SIZE(mcs7830_dongles)) + return 0; + debug("detected USB ethernet device: %04X:%04X\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* fill in driver private data */ + priv = calloc(1, sizeof(*priv)); + if (!priv) + return 0; + + /* fill in the ueth_data structure, attach private data */ + memset(ss, 0, sizeof(*ss)); + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->subclass = iface_desc->bInterfaceSubClass; + ss->protocol = iface_desc->bInterfaceProtocol; + ss->dev_priv = priv; + + /* + * a minimum of three endpoints is expected: in (bulk), + * out (bulk), and interrupt; ignore all others + */ + ep_in_found = ep_out_found = ep_intr_found = 0; + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + uint8_t eptype, epaddr; + bool is_input; + + eptype = iface->ep_desc[i].bmAttributes; + eptype &= USB_ENDPOINT_XFERTYPE_MASK; + + epaddr = iface->ep_desc[i].bEndpointAddress; + is_input = epaddr & USB_DIR_IN; + epaddr &= USB_ENDPOINT_NUMBER_MASK; + + if (eptype == USB_ENDPOINT_XFER_BULK) { + if (is_input && !ep_in_found) { + ss->ep_in = epaddr; + ep_in_found++; + } + if (!is_input && !ep_out_found) { + ss->ep_out = epaddr; + ep_out_found++; + } + } + + if (eptype == USB_ENDPOINT_XFER_INT) { + if (is_input && !ep_intr_found) { + ss->ep_int = epaddr; + ss->irqinterval = iface->ep_desc[i].bInterval; + ep_intr_found++; + } + } + } + debug("endpoints: in %d, out %d, intr %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* apply basic sanity checks */ + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || !ss->ep_int) { + debug("device probe incomplete\n"); + return 0; + } + + dev->privptr = ss; + return 1; +} + +/* + * mcs7830_eth_get_info() - network driver's get_info callback + * @dev: detected USB device + * @ss: USB ethernet data structure filled in at probe() + * @eth: ethernet interface data structure to fill in + * Return: #1 upon success, #0 upon error + * + * this routine registers the mandatory init(), send(), recv(), and + * halt() callbacks with the ethernet interface, can register the + * optional write_hwaddr() callback with the ethernet interface, + * and initiates configuration of the interface such that subsequent + * calls to those callbacks results in network communication + */ +int mcs7830_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + debug("%s()\n", __func__); + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + + snprintf(eth->name, sizeof(eth->name), "%s%d", + MCS7830_BASE_NAME, mcs7830_iface_idx++); + eth->init = mcs7830_init; + eth->send = mcs7830_send; + eth->recv = mcs7830_recv; + eth->halt = mcs7830_halt; + eth->write_hwaddr = mcs7830_write_mac; + eth->priv = ss; + + if (mcs7830_basic_reset(ss->pusb_dev, ss->dev_priv)) + return 0; + + if (mcs7830_read_mac(ss->pusb_dev, eth->enetaddr)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} +#endif + + +#ifdef CONFIG_DM_ETH +static int mcs7830_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + + return mcs7830_init_common(udev); +} + +void mcs7830_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int mcs7830_eth_send(struct udevice *dev, void *packet, int length) +{ + struct mcs7830_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + + return mcs7830_send_common(ueth, packet, length); +} + +int mcs7830_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct mcs7830_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + int len; + + len = mcs7830_recv_common(ueth, priv->rx_buf); + *packetp = priv->rx_buf; + + return len; +} + +static int mcs7830_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct mcs7830_private *priv = dev_get_priv(dev); + + packet_len = ALIGN(packet_len, 4); + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int mcs7830_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + + return mcs7830_write_mac_common(udev, pdata->enetaddr); +} + +static int mcs7830_eth_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct mcs7830_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct ueth_data *ueth = &priv->ueth; + + if (mcs7830_basic_reset(udev, priv)) + return 0; + + if (mcs7830_read_mac(udev, pdata->enetaddr)) + return 0; + + return usb_ether_register(dev, ueth, MCS7830_RX_URB_SIZE); +} + +static const struct eth_ops mcs7830_eth_ops = { + .start = mcs7830_eth_start, + .send = mcs7830_eth_send, + .recv = mcs7830_eth_recv, + .free_pkt = mcs7830_free_pkt, + .stop = mcs7830_eth_stop, + .write_hwaddr = mcs7830_write_hwaddr, +}; + +U_BOOT_DRIVER(mcs7830_eth) = { + .name = "mcs7830_eth", + .id = UCLASS_ETH, + .probe = mcs7830_eth_probe, + .ops = &mcs7830_eth_ops, + .priv_auto = sizeof(struct mcs7830_private), + .plat_auto = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; + +static const struct usb_device_id mcs7830_eth_id_table[] = { + { USB_DEVICE(0x9710, 0x7832) }, /* Moschip 7832 */ + { USB_DEVICE(0x9710, 0x7830), }, /* Moschip 7830 */ + { USB_DEVICE(0x9710, 0x7730), }, /* Moschip 7730 */ + { USB_DEVICE(0x0df6, 0x0021), }, /* Sitecom LN 30 */ + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(mcs7830_eth, mcs7830_eth_id_table); +#endif diff --git a/roms/u-boot/drivers/usb/eth/r8152.c b/roms/u-boot/drivers/usb/eth/r8152.c new file mode 100644 index 000000000..4677da9f2 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/r8152.c @@ -0,0 +1,1897 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. + * + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <net.h> +#include <usb.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include <linux/bitops.h> +#include "usb_ether.h" +#include "r8152.h" + +#ifndef CONFIG_DM_ETH +/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */ + +struct r8152_dongle { + unsigned short vendor; + unsigned short product; +}; + +static const struct r8152_dongle r8152_dongles[] = { + /* Realtek */ + { 0x0bda, 0x8050 }, + { 0x0bda, 0x8152 }, + { 0x0bda, 0x8153 }, + + /* Samsung */ + { 0x04e8, 0xa101 }, + + /* Lenovo */ + { 0x17ef, 0x304f }, + { 0x17ef, 0x3052 }, + { 0x17ef, 0x3054 }, + { 0x17ef, 0x3057 }, + { 0x17ef, 0x7205 }, + { 0x17ef, 0x720a }, + { 0x17ef, 0x720b }, + { 0x17ef, 0x720c }, + + /* TP-LINK */ + { 0x2357, 0x0601 }, + + /* Nvidia */ + { 0x0955, 0x09ff }, +}; +#endif + +struct r8152_version { + unsigned short tcr; + unsigned short version; + bool gmii; +}; + +static const struct r8152_version r8152_versions[] = { + { 0x4c00, RTL_VER_01, 0 }, + { 0x4c10, RTL_VER_02, 0 }, + { 0x5c00, RTL_VER_03, 1 }, + { 0x5c10, RTL_VER_04, 1 }, + { 0x5c20, RTL_VER_05, 1 }, + { 0x5c30, RTL_VER_06, 1 }, + { 0x4800, RTL_VER_07, 0 }, + { 0x6000, RTL_VER_08, 1 }, + { 0x6010, RTL_VER_09, 1 }, +}; + +static +int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) +{ + ALLOC_CACHE_ALIGN_BUFFER(void *, tmp, size); + int ret; + + ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), + RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, + value, index, tmp, size, 500); + memcpy(data, tmp, size); + return ret; +} + +static +int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) +{ + ALLOC_CACHE_ALIGN_BUFFER(void *, tmp, size); + + memcpy(tmp, data, size); + return usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), + RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, + value, index, tmp, size, 500); +} + +int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, + void *data, u16 type) +{ + u16 burst_size = 64; + int ret; + int txsize; + + /* both size and index must be 4 bytes align */ + if ((size & 3) || !size || (index & 3) || !data) + return -EINVAL; + + if (index + size > 0xffff) + return -EINVAL; + + while (size) { + txsize = min(size, burst_size); + ret = get_registers(tp, index, type, txsize, data); + if (ret < 0) + break; + + index += txsize; + data += txsize; + size -= txsize; + } + + return ret; +} + +int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, void *data, u16 type) +{ + int ret; + u16 byteen_start, byteen_end, byte_en_to_hw; + u16 burst_size = 512; + int txsize; + + /* both size and index must be 4 bytes align */ + if ((size & 3) || !size || (index & 3) || !data) + return -EINVAL; + + if (index + size > 0xffff) + return -EINVAL; + + byteen_start = byteen & BYTE_EN_START_MASK; + byteen_end = byteen & BYTE_EN_END_MASK; + + byte_en_to_hw = byteen_start | (byteen_start << 4); + ret = set_registers(tp, index, type | byte_en_to_hw, 4, data); + if (ret < 0) + return ret; + + index += 4; + data += 4; + size -= 4; + + if (size) { + size -= 4; + + while (size) { + txsize = min(size, burst_size); + + ret = set_registers(tp, index, + type | BYTE_EN_DWORD, + txsize, data); + if (ret < 0) + return ret; + + index += txsize; + data += txsize; + size -= txsize; + } + + byte_en_to_hw = byteen_end | (byteen_end >> 4); + ret = set_registers(tp, index, type | byte_en_to_hw, 4, data); + if (ret < 0) + return ret; + } + + return ret; +} + +int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) +{ + return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA); +} + +int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) +{ + return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA); +} + +int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) +{ + return generic_ocp_read(tp, index, size, data, MCU_TYPE_USB); +} + +int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) +{ + return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB); +} + +u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index) +{ + __le32 data; + + generic_ocp_read(tp, index, sizeof(data), &data, type); + + return __le32_to_cpu(data); +} + +void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data) +{ + __le32 tmp = __cpu_to_le32(data); + + generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, type); +} + +u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index) +{ + u32 data; + __le32 tmp; + u8 shift = index & 2; + + index &= ~3; + + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); + + data = __le32_to_cpu(tmp); + data >>= (shift * 8); + data &= 0xffff; + + return data; +} + +void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) +{ + u32 mask = 0xffff; + __le32 tmp; + u16 byen = BYTE_EN_WORD; + u8 shift = index & 2; + + data &= mask; + + if (index & 2) { + byen <<= shift; + mask <<= (shift * 8); + data <<= (shift * 8); + index &= ~3; + } + + tmp = __cpu_to_le32(data); + + generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); +} + +u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index) +{ + u32 data; + __le32 tmp; + u8 shift = index & 3; + + index &= ~3; + + generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); + + data = __le32_to_cpu(tmp); + data >>= (shift * 8); + data &= 0xff; + + return data; +} + +void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) +{ + u32 mask = 0xff; + __le32 tmp; + u16 byen = BYTE_EN_BYTE; + u8 shift = index & 3; + + data &= mask; + + if (index & 3) { + byen <<= shift; + mask <<= (shift * 8); + data <<= (shift * 8); + index &= ~3; + } + + tmp = __cpu_to_le32(data); + + generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); +} + +u16 ocp_reg_read(struct r8152 *tp, u16 addr) +{ + u16 ocp_base, ocp_index; + + ocp_base = addr & 0xf000; + if (ocp_base != tp->ocp_base) { + ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); + tp->ocp_base = ocp_base; + } + + ocp_index = (addr & 0x0fff) | 0xb000; + return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); +} + +void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) +{ + u16 ocp_base, ocp_index; + + ocp_base = addr & 0xf000; + if (ocp_base != tp->ocp_base) { + ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); + tp->ocp_base = ocp_base; + } + + ocp_index = (addr & 0x0fff) | 0xb000; + ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); +} + +static void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) +{ + ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); +} + +static int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) +{ + return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); +} + +void sram_write(struct r8152 *tp, u16 addr, u16 data) +{ + ocp_reg_write(tp, OCP_SRAM_ADDR, addr); + ocp_reg_write(tp, OCP_SRAM_DATA, data); +} + +static u16 sram_read(struct r8152 *tp, u16 addr) +{ + ocp_reg_write(tp, OCP_SRAM_ADDR, addr); + return ocp_reg_read(tp, OCP_SRAM_DATA); +} + +int r8152_wait_for_bit(struct r8152 *tp, bool ocp_reg, u16 type, u16 index, + const u32 mask, bool set, unsigned int timeout) +{ + u32 val; + + while (--timeout) { + if (ocp_reg) + val = ocp_reg_read(tp, index); + else + val = ocp_read_dword(tp, type, index); + + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + mdelay(1); + } + + debug("%s: Timeout (index=%04x mask=%08x timeout=%d)\n", + __func__, index, mask, timeout); + + return -ETIMEDOUT; +} + +static void r8152b_reset_packet_filter(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); + ocp_data &= ~FMC_FCR_MCU_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); + ocp_data |= FMC_FCR_MCU_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); +} + +static void rtl8152_wait_fifo_empty(struct r8152 *tp) +{ + int ret; + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_PHY_PWR, + PLA_PHY_PWR_TXEMP, 1, R8152_WAIT_TIMEOUT); + if (ret) + debug("Timeout waiting for FIFO empty\n"); + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_TCR0, + TCR0_TX_EMPTY, 1, R8152_WAIT_TIMEOUT); + if (ret) + debug("Timeout waiting for TX empty\n"); +} + +static void rtl8152_nic_reset(struct r8152 *tp) +{ + int ret; + u32 ocp_data; + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, BIST_CTRL); + ocp_data |= BIST_CTRL_SW_RESET; + ocp_write_dword(tp, MCU_TYPE_PLA, BIST_CTRL, ocp_data); + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, BIST_CTRL, + BIST_CTRL_SW_RESET, 0, R8152_WAIT_TIMEOUT); + if (ret) + debug("Timeout waiting for NIC reset\n"); +} + +static u8 rtl8152_get_speed(struct r8152 *tp) +{ + return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); +} + +static void rtl_set_eee_plus(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); + ocp_data &= ~EEEP_CR_EEEP_TX; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); +} + +static void rxdy_gated_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); + if (enable) + ocp_data |= RXDY_GATED_EN; + else + ocp_data &= ~RXDY_GATED_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); +} + +static void rtl8152_set_rx_mode(struct r8152 *tp) +{ + u32 ocp_data; + __le32 tmp[2]; + + tmp[0] = 0xffffffff; + tmp[1] = 0xffffffff; + + pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(tmp), tmp); + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data |= RCR_APM | RCR_AM | RCR_AB; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); +} + +static inline void r8153b_rx_agg_chg_indicate(struct r8152 *tp) +{ + ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN, + OWN_UPDATE | OWN_CLEAR); +} + +static int rtl_enable(struct r8152 *tp) +{ + u32 ocp_data; + + r8152b_reset_packet_filter(tp); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); + ocp_data |= PLA_CR_RE | PLA_CR_TE; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); + + switch (tp->version) { + case RTL_VER_08: + case RTL_VER_09: + r8153b_rx_agg_chg_indicate(tp); + break; + default: + break; + } + + rxdy_gated_en(tp, false); + + rtl8152_set_rx_mode(tp); + + return 0; +} + +static int rtl8152_enable(struct r8152 *tp) +{ + rtl_set_eee_plus(tp); + + return rtl_enable(tp); +} + +static void r8153_set_rx_early_timeout(struct r8152 *tp) +{ + u32 ocp_data = tp->coalesce / 8; + + switch (tp->version) { + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, + ocp_data); + break; + + case RTL_VER_08: + case RTL_VER_09: + /* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout + * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 1264ns. + */ + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, + RX_AUXILIARY_TIMER / 8); + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR, + ocp_data); + break; + + default: + debug("** %s Invalid Device\n", __func__); + break; + } +} + +static void r8153_set_rx_early_size(struct r8152 *tp) +{ + u32 ocp_data = (RTL8152_AGG_BUF_SZ - RTL8153_RMS - + sizeof(struct rx_desc)); + + switch (tp->version) { + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 4); + break; + + case RTL_VER_08: + case RTL_VER_09: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 8); + break; + + default: + debug("** %s Invalid Device\n", __func__); + break; + } +} + +static int rtl8153_enable(struct r8152 *tp) +{ + rtl_set_eee_plus(tp); + r8153_set_rx_early_timeout(tp); + r8153_set_rx_early_size(tp); + + return rtl_enable(tp); +} + +static void rtl_disable(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data &= ~RCR_ACPT_ALL; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + + rxdy_gated_en(tp, true); + + rtl8152_wait_fifo_empty(tp); + rtl8152_nic_reset(tp); +} + +static void r8152_power_cut_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); + if (enable) + ocp_data |= POWER_CUT; + else + ocp_data &= ~POWER_CUT; + ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); + ocp_data &= ~RESUME_INDICATE; + ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); +} + +static void rtl_rx_vlan_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); + if (enable) + ocp_data |= CPCR_RX_VLAN; + else + ocp_data &= ~CPCR_RX_VLAN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); +} + +static void r8153_u1u2en(struct r8152 *tp, bool enable) +{ + u8 u1u2[8]; + + if (enable) + memset(u1u2, 0xff, sizeof(u1u2)); + else + memset(u1u2, 0x00, sizeof(u1u2)); + + usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2); +} + +static void r8153b_u1u2en(struct r8152 *tp, bool enable) +{ + u16 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG); + if (enable) + ocp_data |= LPM_U1U2_EN; + else + ocp_data &= ~LPM_U1U2_EN; + + ocp_write_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG, ocp_data); +} + +static void r8153_u2p3en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); + if (enable && tp->version != RTL_VER_03 && tp->version != RTL_VER_04) + ocp_data |= U2P3_ENABLE; + else + ocp_data &= ~U2P3_ENABLE; + ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); +} + +static void r8153_power_cut_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT); + if (enable) + ocp_data |= PWR_EN | PHASE2_EN; + else + ocp_data &= ~(PWR_EN | PHASE2_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); + ocp_data &= ~PCUT_STATUS; + ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); +} + +static void rtl_reset_bmu(struct r8152 *tp) +{ + u8 ocp_data; + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_RESET); + ocp_data &= ~(BMU_RESET_EP_IN | BMU_RESET_EP_OUT); + ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); + ocp_data |= BMU_RESET_EP_IN | BMU_RESET_EP_OUT; + ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); +} + +static int r8152_read_mac(struct r8152 *tp, unsigned char *macaddr) +{ + int ret; + unsigned char enetaddr[8] = {0}; + + ret = pla_ocp_read(tp, PLA_IDR, 8, enetaddr); + if (ret < 0) + return ret; + + memcpy(macaddr, enetaddr, ETH_ALEN); + return 0; +} + +static void r8152b_disable_aldps(struct r8152 *tp) +{ + ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); + mdelay(20); +} + +static void r8152b_enable_aldps(struct r8152 *tp) +{ + ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | + LINKENA | DIS_SDSAVE); +} + +static void rtl8152_disable(struct r8152 *tp) +{ + r8152b_disable_aldps(tp); + rtl_disable(tp); + r8152b_enable_aldps(tp); +} + +static void r8152b_hw_phy_cfg(struct r8152 *tp) +{ + u16 data; + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + r8152b_firmware(tp); +} + +static void rtl8152_reinit_ll(struct r8152 *tp) +{ + u32 ocp_data; + int ret; + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_PHY_PWR, + PLA_PHY_PWR_LLR, 1, R8152_WAIT_TIMEOUT); + if (ret) + debug("Timeout waiting for link list ready\n"); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); + ocp_data |= RE_INIT_LL; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_PHY_PWR, + PLA_PHY_PWR_LLR, 1, R8152_WAIT_TIMEOUT); + if (ret) + debug("Timeout waiting for link list ready\n"); +} + +static void r8152b_exit_oob(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data &= ~RCR_ACPT_ALL; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + + rxdy_gated_en(tp, true); + r8152b_hw_phy_cfg(tp); + + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data &= ~NOW_IS_OOB; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); + ocp_data &= ~MCU_BORW_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); + + rtl8152_reinit_ll(tp); + rtl8152_nic_reset(tp); + + /* rx share fifo credit full threshold */ + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); + + if (tp->udev->speed == USB_SPEED_FULL || + tp->udev->speed == USB_SPEED_LOW) { + /* rx share fifo credit near full threshold */ + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, + RXFIFO_THR2_FULL); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, + RXFIFO_THR3_FULL); + } else { + /* rx share fifo credit near full threshold */ + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, + RXFIFO_THR2_HIGH); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, + RXFIFO_THR3_HIGH); + } + + /* TX share fifo free credit full threshold */ + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); + + ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); + ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); + ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA, + TEST_MODE_DISABLE | TX_SIZE_ADJUST1); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); + ocp_data |= TCR0_AUTO_FIFO; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); +} + +static void r8152b_enter_oob(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data &= ~NOW_IS_OOB; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB); + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB); + + rtl_disable(tp); + + rtl8152_reinit_ll(tp); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); + + rtl_rx_vlan_en(tp, false); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BDC_CR); + ocp_data |= ALDPS_PROXY_MODE; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BDC_CR, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + rxdy_gated_en(tp, false); + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data |= RCR_APM | RCR_AM | RCR_AB; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); +} + +static void r8153_hw_phy_cfg(struct r8152 *tp) +{ + u32 ocp_data; + u16 data; + + if (tp->version == RTL_VER_03 || tp->version == RTL_VER_04 || + tp->version == RTL_VER_05) + ocp_reg_write(tp, OCP_ADC_CFG, CKADSEL_L | ADC_EN | EN_EMI_L); + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + r8153_firmware(tp); + + if (tp->version == RTL_VER_03) { + data = ocp_reg_read(tp, OCP_EEE_CFG); + data &= ~CTAP_SHORT_EN; + ocp_reg_write(tp, OCP_EEE_CFG, data); + } + + data = ocp_reg_read(tp, OCP_POWER_CFG); + data |= EEE_CLKDIV_EN; + ocp_reg_write(tp, OCP_POWER_CFG, data); + + data = ocp_reg_read(tp, OCP_DOWN_SPEED); + data |= EN_10M_BGOFF; + ocp_reg_write(tp, OCP_DOWN_SPEED, data); + data = ocp_reg_read(tp, OCP_POWER_CFG); + data |= EN_10M_PLLOFF; + ocp_reg_write(tp, OCP_POWER_CFG, data); + sram_write(tp, SRAM_IMPEDANCE, 0x0b13); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= PFM_PWM_SWITCH; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + + /* Enable LPF corner auto tune */ + sram_write(tp, SRAM_LPF_CFG, 0xf70f); + + /* Adjust 10M Amplitude */ + sram_write(tp, SRAM_10M_AMP1, 0x00af); + sram_write(tp, SRAM_10M_AMP2, 0x0208); +} + +static u32 r8152_efuse_read(struct r8152 *tp, u8 addr) +{ + u32 ocp_data; + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD, EFUSE_READ_CMD | addr); + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD); + ocp_data = (ocp_data & EFUSE_DATA_BIT16) << 9; /* data of bit16 */ + ocp_data |= ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_DATA); + + return ocp_data; +} + +static void r8153b_hw_phy_cfg(struct r8152 *tp) +{ + u32 ocp_data; + u16 data; + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + /* U1/U2/L1 idle timer. 500 us */ + ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); + + r8153b_firmware(tp); + + data = sram_read(tp, SRAM_GREEN_CFG); + data |= R_TUNE_EN; + sram_write(tp, SRAM_GREEN_CFG, data); + data = ocp_reg_read(tp, OCP_NCTL_CFG); + data |= PGA_RETURN_EN; + ocp_reg_write(tp, OCP_NCTL_CFG, data); + + /* ADC Bias Calibration: + * read efuse offset 0x7d to get a 17-bit data. Remove the dummy/fake + * bit (bit3) to rebuild the real 16-bit data. Write the data to the + * ADC ioffset. + */ + ocp_data = r8152_efuse_read(tp, 0x7d); + ocp_data = ((ocp_data & 0x1fff0) >> 1) | (ocp_data & 0x7); + if (ocp_data != 0xffff) + ocp_reg_write(tp, OCP_ADC_IOFFSET, ocp_data); + + /* ups mode tx-link-pulse timing adjustment: + * rg_saw_cnt = OCP reg 0xC426 Bit[13:0] + * swr_cnt_1ms_ini = 16000000 / rg_saw_cnt + */ + ocp_data = ocp_reg_read(tp, 0xc426); + ocp_data &= 0x3fff; + if (ocp_data) { + u32 swr_cnt_1ms_ini; + + swr_cnt_1ms_ini = (16000000 / ocp_data) & SAW_CNT_1MS_MASK; + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG); + ocp_data = (ocp_data & ~SAW_CNT_1MS_MASK) | swr_cnt_1ms_ini; + ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CFG, ocp_data); + } + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= PFM_PWM_SWITCH; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); +} + +static void r8153_first_init(struct r8152 *tp) +{ + u32 ocp_data; + + rxdy_gated_en(tp, true); + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data &= ~RCR_ACPT_ALL; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + + r8153_hw_phy_cfg(tp); + + rtl8152_nic_reset(tp); + rtl_reset_bmu(tp); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data &= ~NOW_IS_OOB; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); + ocp_data &= ~MCU_BORW_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); + + rtl8152_reinit_ll(tp); + + rtl_rx_vlan_en(tp, false); + + ocp_data = RTL8153_RMS; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data); + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); + ocp_data |= TCR0_AUTO_FIFO; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); + + rtl8152_nic_reset(tp); + + /* rx share fifo credit full threshold */ + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL); + /* TX share fifo free credit full threshold */ + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2); + + /* rx aggregation */ + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); +} + +static void r8153_enter_oob(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data &= ~NOW_IS_OOB; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + rtl_disable(tp); + rtl_reset_bmu(tp); + + rtl8152_reinit_ll(tp); + + ocp_data = RTL8153_RMS; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); + ocp_data &= ~TEREDO_WAKE_MASK; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); + + rtl_rx_vlan_en(tp, false); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BDC_CR); + ocp_data |= ALDPS_PROXY_MODE; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BDC_CR, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + rxdy_gated_en(tp, false); + + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data |= RCR_APM | RCR_AM | RCR_AB; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); +} + +static void r8153_disable_aldps(struct r8152 *tp) +{ + u16 data; + + data = ocp_reg_read(tp, OCP_POWER_CFG); + data &= ~EN_ALDPS; + ocp_reg_write(tp, OCP_POWER_CFG, data); + mdelay(20); +} + +static void rtl8153_disable(struct r8152 *tp) +{ + r8153_disable_aldps(tp); + rtl_disable(tp); + rtl_reset_bmu(tp); +} + +static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) +{ + u16 bmcr, anar, gbcr; + + anar = r8152_mdio_read(tp, MII_ADVERTISE); + anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + if (tp->supports_gmii) { + gbcr = r8152_mdio_read(tp, MII_CTRL1000); + gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + } else { + gbcr = 0; + } + + if (autoneg == AUTONEG_DISABLE) { + if (speed == SPEED_10) { + bmcr = 0; + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + } else if (speed == SPEED_100) { + bmcr = BMCR_SPEED100; + anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; + } else if (speed == SPEED_1000 && tp->supports_gmii) { + bmcr = BMCR_SPEED1000; + gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + return -EINVAL; + } + + if (duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + } else { + if (speed == SPEED_10) { + if (duplex == DUPLEX_FULL) + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + else + anar |= ADVERTISE_10HALF; + } else if (speed == SPEED_100) { + if (duplex == DUPLEX_FULL) { + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; + } else { + anar |= ADVERTISE_10HALF; + anar |= ADVERTISE_100HALF; + } + } else if (speed == SPEED_1000 && tp->supports_gmii) { + if (duplex == DUPLEX_FULL) { + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; + gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + anar |= ADVERTISE_10HALF; + anar |= ADVERTISE_100HALF; + gbcr |= ADVERTISE_1000HALF; + } + } else { + return -EINVAL; + } + + bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET; + } + + if (tp->supports_gmii) + r8152_mdio_write(tp, MII_CTRL1000, gbcr); + + r8152_mdio_write(tp, MII_ADVERTISE, anar); + r8152_mdio_write(tp, MII_BMCR, bmcr); + + return 0; +} + +static void rtl8152_up(struct r8152 *tp) +{ + r8152b_disable_aldps(tp); + r8152b_exit_oob(tp); + r8152b_enable_aldps(tp); +} + +static void rtl8152_down(struct r8152 *tp) +{ + r8152_power_cut_en(tp, false); + r8152b_disable_aldps(tp); + r8152b_enter_oob(tp); + r8152b_enable_aldps(tp); +} + +static void rtl8153_up(struct r8152 *tp) +{ + r8153_u1u2en(tp, false); + r8153_disable_aldps(tp); + r8153_first_init(tp); + r8153_u2p3en(tp, false); +} + +static void rtl8153_down(struct r8152 *tp) +{ + r8153_u1u2en(tp, false); + r8153_u2p3en(tp, false); + r8153_power_cut_en(tp, false); + r8153_disable_aldps(tp); + r8153_enter_oob(tp); +} + +static void rtl8153b_up(struct r8152 *tp) +{ + r8153_first_init(tp); +} + +static void rtl8153b_down(struct r8152 *tp) +{ + r8153_enter_oob(tp); +} + +static void r8152b_get_version(struct r8152 *tp) +{ + u32 ocp_data; + u16 tcr; + int i; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); + tcr = (u16)(ocp_data & VERSION_MASK); + + for (i = 0; i < ARRAY_SIZE(r8152_versions); i++) { + if (tcr == r8152_versions[i].tcr) { + /* Found a supported version */ + tp->version = r8152_versions[i].version; + tp->supports_gmii = r8152_versions[i].gmii; + break; + } + } + + if (tp->version == RTL_VER_UNKNOWN) + debug("r8152 Unknown tcr version 0x%04x\n", tcr); +} + +static void r8152b_enable_fc(struct r8152 *tp) +{ + u16 anar; + anar = r8152_mdio_read(tp, MII_ADVERTISE); + anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + r8152_mdio_write(tp, MII_ADVERTISE, anar); +} + +static void rtl_tally_reset(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY); + ocp_data |= TALLY_RESET; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); +} + +static void r8152b_init(struct r8152 *tp) +{ + u32 ocp_data; + + r8152b_disable_aldps(tp); + + if (tp->version == RTL_VER_01) { + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); + ocp_data &= ~LED_MODE_MASK; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); + } + + r8152_power_cut_en(tp, false); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); + ocp_data &= ~MCU_CLK_RATIO_MASK; + ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; + ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); + ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | + SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_TIMER); + ocp_data |= BIT(15); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_TIMER, ocp_data); + ocp_write_word(tp, MCU_TYPE_USB, 0xcbfc, 0x03e8); + ocp_data &= ~BIT(15); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_TIMER, ocp_data); + + r8152b_enable_fc(tp); + rtl_tally_reset(tp); + + /* enable rx aggregation */ + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); +} + +static void r8153_init(struct r8152 *tp) +{ + int i; + u32 ocp_data; + + r8153_disable_aldps(tp); + r8153_u1u2en(tp, false); + + r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_BOOT_CTRL, + AUTOLOAD_DONE, 1, R8152_WAIT_TIMEOUT); + + for (i = 0; i < R8152_WAIT_TIMEOUT; i++) { + ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; + if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) + break; + + mdelay(1); + } + + r8153_u2p3en(tp, false); + + if (tp->version == RTL_VER_04) { + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2); + ocp_data &= ~pwd_dn_scale_mask; + ocp_data |= pwd_dn_scale(96); + ocp_write_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + } else if (tp->version == RTL_VER_05) { + ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0); + ocp_data &= ~ECM_ALDPS; + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); + if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) + ocp_data &= ~DYNAMIC_BURST; + else + ocp_data |= DYNAMIC_BURST; + ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); + } else if (tp->version == RTL_VER_06) { + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1); + if (ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) + ocp_data &= ~DYNAMIC_BURST; + else + ocp_data |= DYNAMIC_BURST; + ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, ocp_data); + } + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2); + ocp_data |= EP4_FULL_FC; + ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); + ocp_data &= ~TIMER11_EN; + ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); + ocp_data &= ~LED_MODE_MASK; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); + + ocp_data = FIFO_EMPTY_1FB | ROK_EXIT_LPM; + if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) + ocp_data |= LPM_TIMER_500MS; + else + ocp_data |= LPM_TIMER_500US; + ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); + ocp_data &= ~SEN_VAL_MASK; + ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; + ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); + + ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001); + + r8153_power_cut_en(tp, false); + + r8152b_enable_fc(tp); + rtl_tally_reset(tp); +} + +static void r8153b_init(struct r8152 *tp) +{ + u32 ocp_data; + int i; + + r8153_disable_aldps(tp); + r8153b_u1u2en(tp, false); + + r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_BOOT_CTRL, + AUTOLOAD_DONE, 1, R8152_WAIT_TIMEOUT); + + for (i = 0; i < R8152_WAIT_TIMEOUT; i++) { + ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; + if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) + break; + + mdelay(1); + } + + r8153_u2p3en(tp, false); + + /* MSC timer = 0xfff * 8ms = 32760 ms */ + ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); + + r8153_power_cut_en(tp, false); + + /* MAC clock speed down */ + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); + ocp_data |= MAC_CLK_SPDWN_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); + ocp_data &= ~PLA_MCU_SPDWN_EN; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); + + if (tp->version == RTL_VER_09) { + /* Disable Test IO for 32QFN */ + if (ocp_read_byte(tp, MCU_TYPE_PLA, 0xdc00) & BIT(5)) { + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= TEST_IO_OFF; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + } + } + + /* rx aggregation */ + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); + + rtl_tally_reset(tp); + r8153b_hw_phy_cfg(tp); + r8152b_enable_fc(tp); +} + +static void rtl8152_unload(struct r8152 *tp) +{ + if (tp->version != RTL_VER_01) + r8152_power_cut_en(tp, true); +} + +static void rtl8153_unload(struct r8152 *tp) +{ + r8153_power_cut_en(tp, false); +} + +static int rtl_ops_init(struct r8152 *tp) +{ + struct rtl_ops *ops = &tp->rtl_ops; + int ret = 0; + + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + case RTL_VER_07: + ops->init = r8152b_init; + ops->enable = rtl8152_enable; + ops->disable = rtl8152_disable; + ops->up = rtl8152_up; + ops->down = rtl8152_down; + ops->unload = rtl8152_unload; + break; + + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ops->init = r8153_init; + ops->enable = rtl8153_enable; + ops->disable = rtl8153_disable; + ops->up = rtl8153_up; + ops->down = rtl8153_down; + ops->unload = rtl8153_unload; + break; + + case RTL_VER_08: + case RTL_VER_09: + ops->init = r8153b_init; + ops->enable = rtl8153_enable; + ops->disable = rtl8153_disable; + ops->up = rtl8153b_up; + ops->down = rtl8153b_down; + break; + + default: + ret = -ENODEV; + printf("r8152 Unknown Device\n"); + break; + } + + return ret; +} + +static int r8152_init_common(struct r8152 *tp) +{ + u8 speed; + int timeout = 0; + int link_detected; + + debug("** %s()\n", __func__); + + do { + speed = rtl8152_get_speed(tp); + + link_detected = speed & LINK_STATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + mdelay(TIMEOUT_RESOLUTION); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + if (link_detected) { + tp->rtl_ops.enable(tp); + + if (timeout != 0) + printf("done.\n"); + } else { + printf("unable to connect.\n"); + } + + return 0; +} + +static int r8152_send_common(struct ueth_data *ueth, void *packet, int length) +{ + struct usb_device *udev = ueth->pusb_dev; + u32 opts1, opts2 = 0; + int err; + int actual_len; + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, msg, + PKTSIZE + sizeof(struct tx_desc)); + struct tx_desc *tx_desc = (struct tx_desc *)msg; + + debug("** %s(), len %d\n", __func__, length); + + opts1 = length | TX_FS | TX_LS; + + tx_desc->opts2 = cpu_to_le32(opts2); + tx_desc->opts1 = cpu_to_le32(opts1); + + memcpy(msg + sizeof(struct tx_desc), (void *)packet, length); + + err = usb_bulk_msg(udev, usb_sndbulkpipe(udev, ueth->ep_out), + (void *)msg, length + sizeof(struct tx_desc), + &actual_len, USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %zu, actual = %u, err = %d\n", + length + sizeof(struct tx_desc), actual_len, err); + + return err; +} + +#ifndef CONFIG_DM_ETH +static int r8152_init(struct eth_device *eth, struct bd_info *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct r8152 *tp = (struct r8152 *)dev->dev_priv; + + return r8152_init_common(tp); +} + +static int r8152_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return r8152_send_common(dev, packet, length); +} + +static int r8152_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + ALLOC_CACHE_ALIGN_BUFFER(uint8_t, recv_buf, RTL8152_AGG_BUF_SZ); + unsigned char *pkt_ptr; + int err; + int actual_len; + u16 packet_len; + + u32 bytes_process = 0; + struct rx_desc *rx_desc; + + debug("** %s()\n", __func__); + + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + RTL8152_AGG_BUF_SZ, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", RTL8152_AGG_BUF_SZ, + actual_len, err); + if (err != 0) { + debug("Rx: failed to receive\n"); + return -1; + } + if (actual_len > RTL8152_AGG_BUF_SZ) { + debug("Rx: received too many bytes %d\n", actual_len); + return -1; + } + + while (bytes_process < actual_len) { + rx_desc = (struct rx_desc *)(recv_buf + bytes_process); + pkt_ptr = recv_buf + sizeof(struct rx_desc) + bytes_process; + + packet_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; + packet_len -= CRC_SIZE; + + net_process_received_packet(pkt_ptr, packet_len); + + bytes_process += + (packet_len + sizeof(struct rx_desc) + CRC_SIZE); + + if (bytes_process % 8) + bytes_process = bytes_process + 8 - (bytes_process % 8); + } + + return 0; +} + +static void r8152_halt(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct r8152 *tp = (struct r8152 *)dev->dev_priv; + + debug("** %s()\n", __func__); + + tp->rtl_ops.disable(tp); +} + +static int r8152_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct r8152 *tp = (struct r8152 *)dev->dev_priv; + + unsigned char enetaddr[8] = {0}; + + memcpy(enetaddr, eth->enetaddr, ETH_ALEN); + + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); + pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, enetaddr); + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); + + debug("MAC %pM\n", eth->enetaddr); + return 0; +} + +void r8152_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +/* Probe to see if a new device is actually an realtek device */ +int r8152_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + int ep_in_found = 0, ep_out_found = 0; + struct r8152 *tp; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; i < ARRAY_SIZE(r8152_dongles); i++) { + if (dev->descriptor.idVendor == r8152_dongles[i].vendor && + dev->descriptor.idProduct == r8152_dongles[i].product) + /* Found a supported dongle */ + break; + } + + if (i == ARRAY_SIZE(r8152_dongles)) + return 0; + + memset(ss, 0, sizeof(struct ueth_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* 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; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct r8152)); + + if (!ss->dev_priv) + return 0; + + /* + * 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) { + u8 ep_addr = iface->ep_desc[i].bEndpointAddress; + + if (ep_addr & USB_DIR_IN) { + if (!ep_in_found) { + ss->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = 1; + } + } else { + if (!ep_out_found) { + ss->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = 1; + } + } + } + + /* 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"); + goto error; + } + + dev->privptr = (void *)ss; + + tp = ss->dev_priv; + tp->udev = dev; + tp->intf = iface; + + r8152b_get_version(tp); + + if (rtl_ops_init(tp)) + goto error; + + tp->rtl_ops.init(tp); + tp->rtl_ops.up(tp); + + rtl8152_set_speed(tp, AUTONEG_ENABLE, + tp->supports_gmii ? SPEED_1000 : SPEED_100, + DUPLEX_FULL); + + return 1; + +error: + cfree(ss->dev_priv); + ss->dev_priv = 0; + return 0; +} + +int r8152_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + + sprintf(eth->name, "%s#%d", R8152_BASE_NAME, curr_eth_dev++); + eth->init = r8152_init; + eth->send = r8152_send; + eth->recv = r8152_recv; + eth->halt = r8152_halt; + eth->write_hwaddr = r8152_write_hwaddr; + eth->priv = ss; + + /* Get the MAC address */ + if (r8152_read_mac(ss->dev_priv, eth->enetaddr) < 0) + return 0; + + debug("MAC %pM\n", eth->enetaddr); + return 1; +} +#endif /* !CONFIG_DM_ETH */ + +#ifdef CONFIG_DM_ETH +static int r8152_eth_start(struct udevice *dev) +{ + struct r8152 *tp = dev_get_priv(dev); + + debug("** %s (%d)\n", __func__, __LINE__); + + return r8152_init_common(tp); +} + +void r8152_eth_stop(struct udevice *dev) +{ + struct r8152 *tp = dev_get_priv(dev); + + debug("** %s (%d)\n", __func__, __LINE__); + + tp->rtl_ops.disable(tp); +} + +int r8152_eth_send(struct udevice *dev, void *packet, int length) +{ + struct r8152 *tp = dev_get_priv(dev); + + return r8152_send_common(&tp->ueth, packet, length); +} + +int r8152_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct r8152 *tp = dev_get_priv(dev); + struct ueth_data *ueth = &tp->ueth; + uint8_t *ptr; + int ret, len; + struct rx_desc *rx_desc; + u16 packet_len; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, RTL8152_AGG_BUF_SZ); + if (ret) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + rx_desc = (struct rx_desc *)ptr; + packet_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; + packet_len -= CRC_SIZE; + + if (packet_len > len - (sizeof(struct rx_desc) + CRC_SIZE)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + *packetp = ptr + sizeof(struct rx_desc); + return packet_len; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -ENOSPC; +} + +static int r8152_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct r8152 *tp = dev_get_priv(dev); + + packet_len += sizeof(struct rx_desc) + CRC_SIZE; + packet_len = ALIGN(packet_len, 8); + usb_ether_advance_rxbuf(&tp->ueth, packet_len); + + return 0; +} + +static int r8152_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct r8152 *tp = dev_get_priv(dev); + + unsigned char enetaddr[8] = { 0 }; + + debug("** %s (%d)\n", __func__, __LINE__); + memcpy(enetaddr, pdata->enetaddr, ETH_ALEN); + + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); + pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, enetaddr); + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); + + debug("MAC %pM\n", pdata->enetaddr); + return 0; +} + +int r8152_read_rom_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + struct r8152 *tp = dev_get_priv(dev); + + debug("** %s (%d)\n", __func__, __LINE__); + r8152_read_mac(tp, pdata->enetaddr); + return 0; +} + +static int r8152_eth_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct r8152 *tp = dev_get_priv(dev); + struct ueth_data *ueth = &tp->ueth; + int ret; + + tp->udev = udev; + r8152_read_mac(tp, pdata->enetaddr); + + r8152b_get_version(tp); + + ret = rtl_ops_init(tp); + if (ret) + return ret; + + tp->rtl_ops.init(tp); + tp->rtl_ops.up(tp); + + rtl8152_set_speed(tp, AUTONEG_ENABLE, + tp->supports_gmii ? SPEED_1000 : SPEED_100, + DUPLEX_FULL); + + return usb_ether_register(dev, ueth, RTL8152_AGG_BUF_SZ); +} + +static const struct eth_ops r8152_eth_ops = { + .start = r8152_eth_start, + .send = r8152_eth_send, + .recv = r8152_eth_recv, + .free_pkt = r8152_free_pkt, + .stop = r8152_eth_stop, + .write_hwaddr = r8152_write_hwaddr, + .read_rom_hwaddr = r8152_read_rom_hwaddr, +}; + +U_BOOT_DRIVER(r8152_eth) = { + .name = "r8152_eth", + .id = UCLASS_ETH, + .probe = r8152_eth_probe, + .ops = &r8152_eth_ops, + .priv_auto = sizeof(struct r8152), + .plat_auto = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id r8152_eth_id_table[] = { + /* Realtek */ + { USB_DEVICE(0x0bda, 0x8050) }, + { USB_DEVICE(0x0bda, 0x8152) }, + { USB_DEVICE(0x0bda, 0x8153) }, + + /* Samsung */ + { USB_DEVICE(0x04e8, 0xa101) }, + + /* Lenovo */ + { USB_DEVICE(0x17ef, 0x304f) }, + { USB_DEVICE(0x17ef, 0x3052) }, + { USB_DEVICE(0x17ef, 0x3054) }, + { USB_DEVICE(0x17ef, 0x3057) }, + { USB_DEVICE(0x17ef, 0x7205) }, + { USB_DEVICE(0x17ef, 0x720a) }, + { USB_DEVICE(0x17ef, 0x720b) }, + { USB_DEVICE(0x17ef, 0x720c) }, + + /* TP-LINK */ + { USB_DEVICE(0x2357, 0x0601) }, + + /* Nvidia */ + { USB_DEVICE(0x0955, 0x09ff) }, + + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(r8152_eth, r8152_eth_id_table); +#endif /* CONFIG_DM_ETH */ + diff --git a/roms/u-boot/drivers/usb/eth/r8152.h b/roms/u-boot/drivers/usb/eth/r8152.h new file mode 100644 index 000000000..45172c055 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/r8152.h @@ -0,0 +1,683 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. + * + */ + +#ifndef _RTL8152_ETH_H +#define _RTL8152_ETH_H + +#include <linux/bitops.h> +#define R8152_BASE_NAME "r8152" + +#define PLA_IDR 0xc000 +#define PLA_RCR 0xc010 +#define PLA_RMS 0xc016 +#define PLA_RXFIFO_CTRL0 0xc0a0 +#define PLA_RXFIFO_CTRL1 0xc0a4 +#define PLA_RXFIFO_CTRL2 0xc0a8 +#define PLA_DMY_REG0 0xc0b0 +#define PLA_FMC 0xc0b4 +#define PLA_CFG_WOL 0xc0b6 +#define PLA_TEREDO_CFG 0xc0bc +#define PLA_MAR 0xcd00 +#define PLA_BACKUP 0xd000 +#define PLA_BDC_CR 0xd1a0 +#define PLA_TEREDO_TIMER 0xd2cc +#define PLA_REALWOW_TIMER 0xd2e8 +#define PLA_EXTRA_STATUS 0xd398 +#define PLA_EFUSE_DATA 0xdd00 +#define PLA_EFUSE_CMD 0xdd02 +#define PLA_LEDSEL 0xdd90 +#define PLA_LED_FEATURE 0xdd92 +#define PLA_PHYAR 0xde00 +#define PLA_BOOT_CTRL 0xe004 +#define PLA_GPHY_INTR_IMR 0xe022 +#define PLA_EEE_CR 0xe040 +#define PLA_EEEP_CR 0xe080 +#define PLA_MAC_PWR_CTRL 0xe0c0 +#define PLA_MAC_PWR_CTRL2 0xe0ca +#define PLA_MAC_PWR_CTRL3 0xe0cc +#define PLA_MAC_PWR_CTRL4 0xe0ce +#define PLA_WDT6_CTRL 0xe428 +#define PLA_TCR0 0xe610 +#define PLA_TCR1 0xe612 +#define PLA_MTPS 0xe615 +#define PLA_TXFIFO_CTRL 0xe618 +#define PLA_RSTTALLY 0xe800 +#define BIST_CTRL 0xe810 +#define PLA_CR 0xe813 +#define PLA_CRWECR 0xe81c +#define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */ +#define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */ +#define PLA_CONFIG5 0xe822 +#define PLA_PHY_PWR 0xe84c +#define PLA_OOB_CTRL 0xe84f +#define PLA_CPCR 0xe854 +#define PLA_MISC_0 0xe858 +#define PLA_MISC_1 0xe85a +#define PLA_OCP_GPHY_BASE 0xe86c +#define PLA_TALLYCNT 0xe890 +#define PLA_SFF_STS_7 0xe8de +#define PLA_PHYSTATUS 0xe908 +#define PLA_BP_BA 0xfc26 +#define PLA_BP_0 0xfc28 +#define PLA_BP_1 0xfc2a +#define PLA_BP_2 0xfc2c +#define PLA_BP_3 0xfc2e +#define PLA_BP_4 0xfc30 +#define PLA_BP_5 0xfc32 +#define PLA_BP_6 0xfc34 +#define PLA_BP_7 0xfc36 +#define PLA_BP_EN 0xfc38 + +#define USB_USB2PHY 0xb41e +#define USB_SSPHYLINK2 0xb428 +#define USB_U2P3_CTRL 0xb460 +#define USB_CSR_DUMMY1 0xb464 +#define USB_CSR_DUMMY2 0xb466 +#define USB_DEV_STAT 0xb808 +#define USB_CONNECT_TIMER 0xcbf8 +#define USB_MSC_TIMER 0xcbfc +#define USB_BURST_SIZE 0xcfc0 +#define USB_FW_FIX_EN1 0xcfcc +#define USB_LPM_CONFIG 0xcfd8 +#define USB_USB_CTRL 0xd406 +#define USB_PHY_CTRL 0xd408 +#define USB_TX_AGG 0xd40a +#define USB_RX_BUF_TH 0xd40c +#define USB_USB_TIMER 0xd428 +#define USB_RX_EARLY_TIMEOUT 0xd42c +#define USB_RX_EARLY_SIZE 0xd42e +#define USB_PM_CTRL_STATUS 0xd432 /* RTL8153A */ +#define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */ +#define USB_TX_DMA 0xd434 +#define USB_UPT_RXDMA_OWN 0xd437 +#define USB_TOLERANCE 0xd490 +#define USB_LPM_CTRL 0xd41a +#define USB_BMU_RESET 0xd4b0 +#define USB_U1U2_TIMER 0xd4da +#define USB_UPS_CTRL 0xd800 +#define USB_POWER_CUT 0xd80a +#define USB_MISC_0 0xd81a +#define USB_AFE_CTRL2 0xd824 +#define USB_UPS_CFG 0xd842 +#define USB_WDT11_CTRL 0xe43c +#define USB_BP_BA PLA_BP_BA +#define USB_BP(n) (0xfc28 + 2 * (n)) +#define USB_BP_EN PLA_BP_EN /* RTL8153A */ +#define USB_BP2_EN 0xfc48 + +/* OCP Registers */ +#define OCP_ALDPS_CONFIG 0x2010 +#define OCP_EEE_CONFIG1 0x2080 +#define OCP_EEE_CONFIG2 0x2092 +#define OCP_EEE_CONFIG3 0x2094 +#define OCP_BASE_MII 0xa400 +#define OCP_EEE_AR 0xa41a +#define OCP_EEE_DATA 0xa41c +#define OCP_PHY_STATUS 0xa420 +#define OCP_NCTL_CFG 0xa42c +#define OCP_POWER_CFG 0xa430 +#define OCP_EEE_CFG 0xa432 +#define OCP_SRAM_ADDR 0xa436 +#define OCP_SRAM_DATA 0xa438 +#define OCP_DOWN_SPEED 0xa442 +#define OCP_EEE_ABLE 0xa5c4 +#define OCP_EEE_ADV 0xa5d0 +#define OCP_EEE_LPABLE 0xa5d2 +#define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ +#define OCP_ADC_IOFFSET 0xbcfc +#define OCP_ADC_CFG 0xbc06 + +/* SRAM Register */ +#define SRAM_GREEN_CFG 0x8011 +#define SRAM_LPF_CFG 0x8012 +#define SRAM_10M_AMP1 0x8080 +#define SRAM_10M_AMP2 0x8082 +#define SRAM_IMPEDANCE 0x8084 + +/* PLA_RCR */ +#define RCR_AAP 0x00000001 +#define RCR_APM 0x00000002 +#define RCR_AM 0x00000004 +#define RCR_AB 0x00000008 +#define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) + +/* PLA_RXFIFO_CTRL0 */ +#define RXFIFO_THR1_NORMAL 0x00080002 +#define RXFIFO_THR1_OOB 0x01800003 + +/* PLA_RXFIFO_CTRL1 */ +#define RXFIFO_THR2_FULL 0x00000060 +#define RXFIFO_THR2_HIGH 0x00000038 +#define RXFIFO_THR2_OOB 0x0000004a +#define RXFIFO_THR2_NORMAL 0x00a0 + +/* PLA_RXFIFO_CTRL2 */ +#define RXFIFO_THR3_FULL 0x00000078 +#define RXFIFO_THR3_HIGH 0x00000048 +#define RXFIFO_THR3_OOB 0x0000005a +#define RXFIFO_THR3_NORMAL 0x0110 + +/* PLA_TXFIFO_CTRL */ +#define TXFIFO_THR_NORMAL 0x00400008 +#define TXFIFO_THR_NORMAL2 0x01000008 + +/* PLA_DMY_REG0 */ +#define ECM_ALDPS 0x0002 + +/* PLA_FMC */ +#define FMC_FCR_MCU_EN 0x0001 + +/* PLA_EEEP_CR */ +#define EEEP_CR_EEEP_TX 0x0002 + +/* PLA_WDT6_CTRL */ +#define WDT6_SET_MODE 0x0010 + +/* PLA_TCR0 */ +#define TCR0_TX_EMPTY 0x0800 +#define TCR0_AUTO_FIFO 0x0080 + +/* PLA_TCR1 */ +#define VERSION_MASK 0x7cf0 + +/* PLA_MTPS */ +#define MTPS_JUMBO (12 * 1024 / 64) +#define MTPS_DEFAULT (6 * 1024 / 64) + +/* PLA_RSTTALLY */ +#define TALLY_RESET 0x0001 + +/* PLA_CR */ +#define PLA_CR_RST 0x10 +#define PLA_CR_RE 0x08 +#define PLA_CR_TE 0x04 + +/* PLA_BIST_CTRL */ +#define BIST_CTRL_SW_RESET (0x10 << 24) + +/* PLA_CRWECR */ +#define CRWECR_NORAML 0x00 +#define CRWECR_CONFIG 0xc0 + +/* PLA_OOB_CTRL */ +#define NOW_IS_OOB 0x80 +#define TXFIFO_EMPTY 0x20 +#define RXFIFO_EMPTY 0x10 +#define LINK_LIST_READY 0x02 +#define DIS_MCU_CLROOB 0x01 +#define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY) + +/* PLA_PHY_PWR */ +#define PLA_PHY_PWR_LLR (LINK_LIST_READY << 24) +#define PLA_PHY_PWR_TXEMP (TXFIFO_EMPTY << 24) +#define TEST_IO_OFF BIT(4) + +/* PLA_MISC_1 */ +#define RXDY_GATED_EN 0x0008 + +/* PLA_SFF_STS_7 */ +#define RE_INIT_LL 0x8000 +#define MCU_BORW_EN 0x4000 + +/* PLA_CPCR */ +#define CPCR_RX_VLAN 0x0040 + +/* PLA_CFG_WOL */ +#define MAGIC_EN 0x0001 + +/* PLA_TEREDO_CFG */ +#define TEREDO_SEL 0x8000 +#define TEREDO_WAKE_MASK 0x7f00 +#define TEREDO_RS_EVENT_MASK 0x00fe +#define OOB_TEREDO_EN 0x0001 + +/* PLA_BDC_CR */ +#define ALDPS_PROXY_MODE 0x0001 + +/* PLA_EFUSE_CMD */ +#define EFUSE_READ_CMD BIT(15) +#define EFUSE_DATA_BIT16 BIT(7) + +/* PLA_CONFIG34 */ +#define LINK_ON_WAKE_EN 0x0010 +#define LINK_OFF_WAKE_EN 0x0008 + +/* PLA_CONFIG5 */ +#define BWF_EN 0x0040 +#define MWF_EN 0x0020 +#define UWF_EN 0x0010 +#define LAN_WAKE_EN 0x0002 + +/* PLA_LED_FEATURE */ +#define LED_MODE_MASK 0x0700 + +/* PLA_PHY_PWR */ +#define TX_10M_IDLE_EN 0x0080 +#define PFM_PWM_SWITCH 0x0040 + +/* PLA_MAC_PWR_CTRL */ +#define D3_CLK_GATED_EN 0x00004000 +#define MCU_CLK_RATIO 0x07010f07 +#define MCU_CLK_RATIO_MASK 0x0f0f0f0f +#define ALDPS_SPDWN_RATIO 0x0f87 + +/* PLA_MAC_PWR_CTRL2 */ +#define EEE_SPDWN_RATIO 0x8007 +#define MAC_CLK_SPDWN_EN BIT(15) + +/* PLA_MAC_PWR_CTRL3 */ +#define PLA_MCU_SPDWN_EN BIT(14) +#define PKT_AVAIL_SPDWN_EN 0x0100 +#define SUSPEND_SPDWN_EN 0x0004 +#define U1U2_SPDWN_EN 0x0002 +#define L1_SPDWN_EN 0x0001 + +/* PLA_MAC_PWR_CTRL4 */ +#define PWRSAVE_SPDWN_EN 0x1000 +#define RXDV_SPDWN_EN 0x0800 +#define TX10MIDLE_EN 0x0100 +#define TP100_SPDWN_EN 0x0020 +#define TP500_SPDWN_EN 0x0010 +#define TP1000_SPDWN_EN 0x0008 +#define EEE_SPDWN_EN 0x0001 + +/* PLA_GPHY_INTR_IMR */ +#define GPHY_STS_MSK 0x0001 +#define SPEED_DOWN_MSK 0x0002 +#define SPDWN_RXDV_MSK 0x0004 +#define SPDWN_LINKCHG_MSK 0x0008 + +/* PLA_PHYAR */ +#define PHYAR_FLAG 0x80000000 + +/* PLA_EEE_CR */ +#define EEE_RX_EN 0x0001 +#define EEE_TX_EN 0x0002 + +/* PLA_BOOT_CTRL */ +#define AUTOLOAD_DONE 0x0002 + +/* PLA_EXTRA_STATUS */ +#define U3P3_CHECK_EN BIT(7) + +/* USB_USB2PHY */ +#define USB2PHY_SUSPEND 0x0001 +#define USB2PHY_L1 0x0002 + +/* USB_SSPHYLINK2 */ +#define pwd_dn_scale_mask 0x3ffe +#define pwd_dn_scale(x) ((x) << 1) + +/* USB_CSR_DUMMY1 */ +#define DYNAMIC_BURST 0x0001 + +/* USB_CSR_DUMMY2 */ +#define EP4_FULL_FC 0x0001 + +/* USB_DEV_STAT */ +#define STAT_SPEED_MASK 0x0006 +#define STAT_SPEED_HIGH 0x0000 +#define STAT_SPEED_FULL 0x0002 + +/* USB_FW_FIX_EN1 */ +#define FW_IP_RESET_EN BIT(9) + +/* USB_LPM_CONFIG */ +#define LPM_U1U2_EN BIT(0) + +/* USB_TX_AGG */ +#define TX_AGG_MAX_THRESHOLD 0x03 + +/* USB_RX_BUF_TH */ +#define RX_THR_SUPPER 0x0c350180 +#define RX_THR_HIGH 0x7a120180 +#define RX_THR_SLOW 0xffff0180 + +/* USB_RX_EARLY_TIMEOUT */ +#define RX_AUXILIARY_TIMER 1264 + +/* USB_TX_DMA */ +#define TEST_MODE_DISABLE 0x00000001 +#define TX_SIZE_ADJUST1 0x00000100 + +/* USB_BMU_RESET */ +#define BMU_RESET_EP_IN 0x01 +#define BMU_RESET_EP_OUT 0x02 + +/* USB_UPT_RXDMA_OWN */ +#define OWN_UPDATE BIT(0) +#define OWN_CLEAR BIT(1) + +/* USB_UPS_CTRL */ +#define POWER_CUT 0x0100 + +/* USB_PM_CTRL_STATUS */ +#define RESUME_INDICATE 0x0001 + +/* USB_USB_CTRL */ +#define RX_AGG_DISABLE 0x0010 +#define RX_ZERO_EN 0x0080 + +/* USB_U2P3_CTRL */ +#define U2P3_ENABLE 0x0001 + +/* USB_POWER_CUT */ +#define PWR_EN 0x0001 +#define PHASE2_EN 0x0008 + +/* USB_MISC_0 */ +#define PCUT_STATUS 0x0001 + +/* USB_RX_EARLY_TIMEOUT */ +#define COALESCE_SUPER 85000U +#define COALESCE_HIGH 250000U +#define COALESCE_SLOW 524280U + +/* USB_WDT11_CTRL */ +#define TIMER11_EN 0x0001 + +/* USB_LPM_CTRL */ +/* bit 4 ~ 5: fifo empty boundary */ +#define FIFO_EMPTY_1FB 0x30 /* 0x1fb * 64 = 32448 bytes */ +/* bit 2 ~ 3: LMP timer */ +#define LPM_TIMER_MASK 0x0c +#define LPM_TIMER_500MS 0x04 /* 500 ms */ +#define LPM_TIMER_500US 0x0c /* 500 us */ +#define ROK_EXIT_LPM 0x02 + +/* USB_AFE_CTRL2 */ +#define SEN_VAL_MASK 0xf800 +#define SEN_VAL_NORMAL 0xa000 +#define SEL_RXIDLE 0x0100 + +/* USB_UPS_CFG */ +#define SAW_CNT_1MS_MASK 0x0fff + +/* OCP_ALDPS_CONFIG */ +#define ENPWRSAVE 0x8000 +#define ENPDNPS 0x0200 +#define LINKENA 0x0100 +#define DIS_SDSAVE 0x0010 + +/* OCP_PHY_STATUS */ +#define PHY_STAT_MASK 0x0007 +#define PHY_STAT_LAN_ON 3 +#define PHY_STAT_PWRDN 5 + +/* OCP_NCTL_CFG */ +#define PGA_RETURN_EN BIT(1) + +/* OCP_POWER_CFG */ +#define EEE_CLKDIV_EN 0x8000 +#define EN_ALDPS 0x0004 +#define EN_10M_PLLOFF 0x0001 + +/* OCP_EEE_CONFIG1 */ +#define RG_TXLPI_MSK_HFDUP 0x8000 +#define RG_MATCLR_EN 0x4000 +#define EEE_10_CAP 0x2000 +#define EEE_NWAY_EN 0x1000 +#define TX_QUIET_EN 0x0200 +#define RX_QUIET_EN 0x0100 +#define sd_rise_time_mask 0x0070 +#define sd_rise_time(x) (min((x), 7) << 4) /* bit 4 ~ 6 */ +#define RG_RXLPI_MSK_HFDUP 0x0008 +#define SDFALLTIME 0x0007 /* bit 0 ~ 2 */ + +/* OCP_EEE_CONFIG2 */ +#define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */ +#define RG_DACQUIET_EN 0x0400 +#define RG_LDVQUIET_EN 0x0200 +#define RG_CKRSEL 0x0020 +#define RG_EEEPRG_EN 0x0010 + +/* OCP_EEE_CONFIG3 */ +#define fast_snr_mask 0xff80 +#define fast_snr(x) (min((x), 0x1ff) << 7) /* bit 7 ~ 15 */ +#define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */ +#define MSK_PH 0x0006 /* bit 0 ~ 3 */ + +/* OCP_EEE_AR */ +/* bit[15:14] function */ +#define FUN_ADDR 0x0000 +#define FUN_DATA 0x4000 +/* bit[4:0] device addr */ + +/* OCP_EEE_CFG */ +#define CTAP_SHORT_EN 0x0040 +#define EEE10_EN 0x0010 + +/* OCP_DOWN_SPEED */ +#define EN_10M_BGOFF 0x0080 + +/* OCP_PHY_STATE */ +#define TXDIS_STATE 0x01 +#define ABD_STATE 0x02 + +/* OCP_ADC_CFG */ +#define CKADSEL_L 0x0100 +#define ADC_EN 0x0080 +#define EN_EMI_L 0x0040 + +/* SRAM_GREEN_CFG */ +#define GREEN_ETH_EN BIT(15) +#define R_TUNE_EN BIT(11) + +/* SRAM_LPF_CFG */ +#define LPF_AUTO_TUNE 0x8000 + +/* SRAM_10M_AMP1 */ +#define GDAC_IB_UPALL 0x0008 + +/* SRAM_10M_AMP2 */ +#define AMP_DN 0x0200 + +/* SRAM_IMPEDANCE */ +#define RX_DRIVING_MASK 0x6000 + +#define RTL8152_MAX_TX 4 +#define RTL8152_MAX_RX 10 +#define INTBUFSIZE 2 +#define CRC_SIZE 4 +#define TX_ALIGN 4 +#define RX_ALIGN 8 + +#define INTR_LINK 0x0004 + +#define RTL8152_REQT_READ 0xc0 +#define RTL8152_REQT_WRITE 0x40 +#define RTL8152_REQ_GET_REGS 0x05 +#define RTL8152_REQ_SET_REGS 0x05 + +#define BYTE_EN_DWORD 0xff +#define BYTE_EN_WORD 0x33 +#define BYTE_EN_BYTE 0x11 +#define BYTE_EN_SIX_BYTES 0x3f +#define BYTE_EN_START_MASK 0x0f +#define BYTE_EN_END_MASK 0xf0 + +#define RTL8152_ETH_FRAME_LEN 1514 +#define RTL8152_AGG_BUF_SZ 2048 + +#define RTL8152_RMS (RTL8152_ETH_FRAME_LEN + CRC_SIZE) +#define RTL8153_RMS (RTL8152_ETH_FRAME_LEN + CRC_SIZE) +#define RTL8152_TX_TIMEOUT (5 * HZ) + +#define MCU_TYPE_PLA 0x0100 +#define MCU_TYPE_USB 0x0000 + +/* The forced speed, 10Mb, 100Mb, gigabit. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 + +#define SPEED_UNKNOWN -1 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 +#define DUPLEX_UNKNOWN 0xff + +/* Enable or disable autonegotiation. */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +/* Generic MII registers. */ +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +#define MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ +#define MII_ESTATUS 0x0f /* Extended Status */ +#define MII_DCOUNTER 0x12 /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define MII_SREVISION 0x16 /* Silicon revision */ +#define MII_RESV1 0x17 /* Reserved... */ +#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19 /* PHY address */ +#define MII_RESV2 0x1a /* Reserved... */ +#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1c /* Network interface config */ + +#define TIMEOUT_RESOLUTION 50 +#define PHY_CONNECT_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 +#define R8152_WAIT_TIMEOUT 2000 + +struct rx_desc { + __le32 opts1; +#define RD_CRC BIT(15) +#define RX_LEN_MASK 0x7fff + + __le32 opts2; +#define RD_UDP_CS BIT(23) +#define RD_TCP_CS BIT(22) +#define RD_IPV6_CS BIT(20) +#define RD_IPV4_CS BIT(19) + + __le32 opts3; +#define IPF BIT(23) /* IP checksum fail */ +#define UDPF BIT(22) /* UDP checksum fail */ +#define TCPF BIT(21) /* TCP checksum fail */ +#define RX_VLAN_TAG BIT(16) + + __le32 opts4; + __le32 opts5; + __le32 opts6; +}; + +struct tx_desc { + __le32 opts1; +#define TX_FS BIT(31) /* First segment of a packet */ +#define TX_LS BIT(30) /* Final segment of a packet */ +#define LGSEND BIT(29) +#define GTSENDV4 BIT(28) +#define GTSENDV6 BIT(27) +#define GTTCPHO_SHIFT 18 +#define GTTCPHO_MAX 0x7fU +#define TX_LEN_MAX 0x3ffffU + + __le32 opts2; +#define UDP_CS BIT(31) /* Calculate UDP/IP checksum */ +#define TCP_CS BIT(30) /* Calculate TCP/IP checksum */ +#define IPV4_CS BIT(29) /* Calculate IPv4 checksum */ +#define IPV6_CS BIT(28) /* Calculate IPv6 checksum */ +#define MSS_SHIFT 17 +#define MSS_MAX 0x7ffU +#define TCPHO_SHIFT 17 +#define TCPHO_MAX 0x7ffU +#define TX_VLAN_TAG BIT(16) +}; + +enum rtl_version { + RTL_VER_UNKNOWN = 0, + RTL_VER_01, + RTL_VER_02, + RTL_VER_03, + RTL_VER_04, + RTL_VER_05, + RTL_VER_06, + RTL_VER_07, + RTL_VER_08, + RTL_VER_09, + RTL_VER_MAX +}; + +enum rtl_register_content { + _1000bps = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LINK_STATUS = 0x02, + FULL_DUP = 0x01, +}; + +struct r8152 { + struct usb_device *udev; + struct usb_interface *intf; + bool supports_gmii; + + struct rtl_ops { + void (*init)(struct r8152 *); + int (*enable)(struct r8152 *); + void (*disable)(struct r8152 *); + void (*up)(struct r8152 *); + void (*down)(struct r8152 *); + void (*unload)(struct r8152 *); + } rtl_ops; + + u32 coalesce; + u16 ocp_base; + + u8 version; + +#ifdef CONFIG_DM_ETH + struct ueth_data ueth; +#endif +}; + +int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, void *data, u16 type); +int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, + void *data, u16 type); + +int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data); +int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, void *data); + +int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data); +int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, void *data); + +u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index); +void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data); + +u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index); +void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data); + +u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index); +void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data); + +u16 ocp_reg_read(struct r8152 *tp, u16 addr); +void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data); + +void sram_write(struct r8152 *tp, u16 addr, u16 data); + +int r8152_wait_for_bit(struct r8152 *tp, bool ocp_reg, u16 type, u16 index, + const u32 mask, bool set, unsigned int timeout); + +void r8152b_firmware(struct r8152 *tp); +void r8153_firmware(struct r8152 *tp); +void r8153b_firmware(struct r8152 *tp); +#endif diff --git a/roms/u-boot/drivers/usb/eth/r8152_fw.c b/roms/u-boot/drivers/usb/eth/r8152_fw.c new file mode 100644 index 000000000..a41abed30 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/r8152_fw.c @@ -0,0 +1,1194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. + * + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include "usb_ether.h" +#include "r8152.h" + +static u8 r8152b_pla_patch_a[] = { + 0x08, 0xe0, 0x40, 0xe0, 0x78, 0xe0, 0x85, 0xe0, + 0x5d, 0xe1, 0xa1, 0xe1, 0xa3, 0xe1, 0xab, 0xe1, + 0x31, 0xc3, 0x60, 0x72, 0xa0, 0x49, 0x10, 0xf0, + 0xa4, 0x49, 0x0e, 0xf0, 0x2c, 0xc3, 0x62, 0x72, + 0x26, 0x70, 0x80, 0x49, 0x05, 0xf0, 0x2f, 0x48, + 0x62, 0x9a, 0x24, 0x70, 0x60, 0x98, 0x24, 0xc3, + 0x60, 0x99, 0x23, 0xc3, 0x00, 0xbb, 0x2c, 0x75, + 0xdc, 0x21, 0xbc, 0x25, 0x04, 0x13, 0x0a, 0xf0, + 0x03, 0x13, 0x08, 0xf0, 0x02, 0x13, 0x06, 0xf0, + 0x01, 0x13, 0x04, 0xf0, 0x08, 0x13, 0x02, 0xf0, + 0x03, 0xe0, 0xd4, 0x49, 0x04, 0xf1, 0x14, 0xc2, + 0x12, 0xc3, 0x00, 0xbb, 0x12, 0xc3, 0x60, 0x75, + 0xd0, 0x49, 0x05, 0xf1, 0x50, 0x48, 0x60, 0x9d, + 0x09, 0xc6, 0x00, 0xbe, 0xd0, 0x48, 0x60, 0x9d, + 0xf3, 0xe7, 0xc2, 0xc0, 0x38, 0xd2, 0xc6, 0xd2, + 0x84, 0x17, 0xa2, 0x13, 0x0c, 0x17, 0xbc, 0xc0, + 0xa2, 0xd1, 0x33, 0xc5, 0xa0, 0x74, 0xc0, 0x49, + 0x1f, 0xf0, 0x30, 0xc5, 0xa0, 0x73, 0x00, 0x13, + 0x04, 0xf1, 0xa2, 0x73, 0x00, 0x13, 0x14, 0xf0, + 0x28, 0xc5, 0xa0, 0x74, 0xc8, 0x49, 0x1b, 0xf1, + 0x26, 0xc5, 0xa0, 0x76, 0xa2, 0x74, 0x01, 0x06, + 0x20, 0x37, 0xa0, 0x9e, 0xa2, 0x9c, 0x1e, 0xc5, + 0xa2, 0x73, 0x23, 0x40, 0x10, 0xf8, 0x04, 0xf3, + 0xa0, 0x73, 0x33, 0x40, 0x0c, 0xf8, 0x15, 0xc5, + 0xa0, 0x74, 0x41, 0x48, 0xa0, 0x9c, 0x14, 0xc5, + 0xa0, 0x76, 0x62, 0x48, 0xe0, 0x48, 0xa0, 0x9e, + 0x10, 0xc6, 0x00, 0xbe, 0x0a, 0xc5, 0xa0, 0x74, + 0x48, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0x20, 0x1e, + 0xa0, 0x9e, 0xe5, 0x48, 0xa0, 0x9e, 0xf0, 0xe7, + 0xbc, 0xc0, 0xc8, 0xd2, 0xcc, 0xd2, 0x28, 0xe4, + 0x22, 0x02, 0xf0, 0xc0, 0x0b, 0xc0, 0x00, 0x71, + 0x0a, 0xc0, 0x00, 0x72, 0xa0, 0x49, 0x04, 0xf0, + 0xa4, 0x49, 0x02, 0xf0, 0x93, 0x48, 0x04, 0xc0, + 0x00, 0xb8, 0x00, 0xe4, 0xc2, 0xc0, 0x8c, 0x09, + 0x14, 0xc2, 0x40, 0x73, 0xba, 0x48, 0x40, 0x9b, + 0x11, 0xc2, 0x40, 0x73, 0xb0, 0x49, 0x17, 0xf0, + 0xbf, 0x49, 0x03, 0xf1, 0x09, 0xc5, 0x00, 0xbd, + 0xb1, 0x49, 0x11, 0xf0, 0xb1, 0x48, 0x40, 0x9b, + 0x02, 0xc2, 0x00, 0xba, 0x82, 0x18, 0x00, 0xa0, + 0x1e, 0xfc, 0xbc, 0xc0, 0xf0, 0xc0, 0xde, 0xe8, + 0x00, 0x80, 0x00, 0x60, 0x2c, 0x75, 0xd4, 0x49, + 0x12, 0xf1, 0x29, 0xe0, 0xf8, 0xc2, 0x46, 0x71, + 0xf7, 0xc2, 0x40, 0x73, 0xbe, 0x49, 0x03, 0xf1, + 0xf5, 0xc7, 0x02, 0xe0, 0xf2, 0xc7, 0x4f, 0x30, + 0x26, 0x62, 0xa1, 0x49, 0xf0, 0xf1, 0x22, 0x72, + 0xa0, 0x49, 0xed, 0xf1, 0x25, 0x25, 0x18, 0x1f, + 0x97, 0x30, 0x91, 0x30, 0x36, 0x9a, 0x2c, 0x75, + 0x32, 0xc3, 0x60, 0x73, 0xb1, 0x49, 0x0d, 0xf1, + 0xdc, 0x21, 0xbc, 0x25, 0x27, 0xc6, 0xc0, 0x77, + 0x04, 0x13, 0x18, 0xf0, 0x03, 0x13, 0x19, 0xf0, + 0x02, 0x13, 0x1a, 0xf0, 0x01, 0x13, 0x1b, 0xf0, + 0xd4, 0x49, 0x03, 0xf1, 0x1c, 0xc5, 0x00, 0xbd, + 0xcd, 0xc6, 0xc6, 0x67, 0x2e, 0x75, 0xd7, 0x22, + 0xdd, 0x26, 0x05, 0x15, 0x1a, 0xf0, 0x14, 0xc6, + 0x00, 0xbe, 0x13, 0xc5, 0x00, 0xbd, 0x12, 0xc5, + 0x00, 0xbd, 0xf1, 0x49, 0xfb, 0xf1, 0xef, 0xe7, + 0xf4, 0x49, 0xfa, 0xf1, 0xec, 0xe7, 0xf3, 0x49, + 0xf7, 0xf1, 0xe9, 0xe7, 0xf2, 0x49, 0xf4, 0xf1, + 0xe6, 0xe7, 0xb6, 0xc0, 0x6a, 0x14, 0xac, 0x13, + 0xd6, 0x13, 0xfa, 0x14, 0xa0, 0xd1, 0x00, 0x00, + 0xc0, 0x75, 0xd0, 0x49, 0x46, 0xf0, 0x26, 0x72, + 0xa7, 0x49, 0x43, 0xf0, 0x22, 0x72, 0x25, 0x25, + 0x20, 0x1f, 0x97, 0x30, 0x91, 0x30, 0x40, 0x73, + 0xf3, 0xc4, 0x1c, 0x40, 0x04, 0xf0, 0xd7, 0x49, + 0x05, 0xf1, 0x37, 0xe0, 0x53, 0x48, 0xc0, 0x9d, + 0x08, 0x02, 0x40, 0x66, 0x64, 0x27, 0x06, 0x16, + 0x30, 0xf1, 0x46, 0x63, 0x3b, 0x13, 0x2d, 0xf1, + 0x34, 0x9b, 0x18, 0x1b, 0x93, 0x30, 0x2b, 0xc3, + 0x10, 0x1c, 0x2b, 0xe8, 0x01, 0x14, 0x25, 0xf1, + 0x00, 0x1d, 0x26, 0x1a, 0x8a, 0x30, 0x22, 0x73, + 0xb5, 0x25, 0x0e, 0x0b, 0x00, 0x1c, 0x2c, 0xe8, + 0x1f, 0xc7, 0x27, 0x40, 0x1a, 0xf1, 0x38, 0xe8, + 0x32, 0x1f, 0x8f, 0x30, 0x08, 0x1b, 0x24, 0xe8, + 0x36, 0x72, 0x46, 0x77, 0x00, 0x17, 0x0d, 0xf0, + 0x13, 0xc3, 0x1f, 0x40, 0x03, 0xf1, 0x00, 0x1f, + 0x46, 0x9f, 0x44, 0x77, 0x9f, 0x44, 0x5f, 0x44, + 0x17, 0xe8, 0x0a, 0xc7, 0x27, 0x40, 0x05, 0xf1, + 0x02, 0xc3, 0x00, 0xbb, 0x50, 0x1a, 0x06, 0x1a, + 0xff, 0xc7, 0x00, 0xbf, 0xb8, 0xcd, 0xff, 0xff, + 0x02, 0x0c, 0x54, 0xa5, 0xdc, 0xa5, 0x2f, 0x40, + 0x05, 0xf1, 0x00, 0x14, 0xfa, 0xf1, 0x01, 0x1c, + 0x02, 0xe0, 0x00, 0x1c, 0x80, 0xff, 0xb0, 0x49, + 0x04, 0xf0, 0x01, 0x0b, 0xd3, 0xa1, 0x03, 0xe0, + 0x02, 0x0b, 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, + 0x02, 0x0b, 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, + 0x00, 0x13, 0xfb, 0xf1, 0x80, 0xff, 0x22, 0x73, + 0xb5, 0x25, 0x18, 0x1e, 0xde, 0x30, 0xd9, 0x30, + 0x64, 0x72, 0x11, 0x1e, 0x68, 0x23, 0x16, 0x31, + 0x80, 0xff, 0xd4, 0x49, 0x28, 0xf0, 0x02, 0xb4, + 0x2a, 0xc4, 0x00, 0x1d, 0x2e, 0xe8, 0xe0, 0x73, + 0xb9, 0x21, 0xbd, 0x25, 0x04, 0x13, 0x02, 0xf0, + 0x1a, 0xe0, 0x22, 0xc4, 0x23, 0xc3, 0x2f, 0xe8, + 0x23, 0xc3, 0x2d, 0xe8, 0x00, 0x1d, 0x21, 0xe8, + 0xe2, 0x73, 0xbb, 0x49, 0xfc, 0xf0, 0xe0, 0x73, + 0xb7, 0x48, 0x03, 0xb4, 0x81, 0x1d, 0x19, 0xe8, + 0x40, 0x1a, 0x84, 0x1d, 0x16, 0xe8, 0x12, 0xc3, + 0x1e, 0xe8, 0x03, 0xb0, 0x81, 0x1d, 0x11, 0xe8, + 0x0e, 0xc3, 0x19, 0xe8, 0x02, 0xb0, 0x06, 0xc7, + 0x04, 0x1e, 0xe0, 0x9e, 0x02, 0xc6, 0x00, 0xbe, + 0x22, 0x02, 0x20, 0xe4, 0x04, 0xb8, 0x34, 0xb0, + 0x00, 0x02, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x0c, + 0x09, 0xc7, 0xe0, 0x9b, 0xe2, 0x9a, 0xe4, 0x9c, + 0xe6, 0x8d, 0xe6, 0x76, 0xef, 0x49, 0xfe, 0xf1, + 0x80, 0xff, 0x08, 0xea, 0x82, 0x1d, 0xf5, 0xef, + 0x00, 0x1a, 0x88, 0x1d, 0xf2, 0xef, 0xed, 0xc2, + 0xf0, 0xef, 0x80, 0xff, 0x02, 0xc6, 0x00, 0xbe, + 0x46, 0x06, 0x08, 0xc2, 0x40, 0x73, 0x3a, 0x48, + 0x40, 0x9b, 0x06, 0xff, 0x02, 0xc6, 0x00, 0xbe, + 0x86, 0x17, 0x1e, 0xfc, 0x36, 0xf0, 0x08, 0x1c, + 0xea, 0x8c, 0xe3, 0x64, 0xc7, 0x49, 0x25, 0xf1, + 0xe0, 0x75, 0xff, 0x1b, 0xeb, 0x47, 0xff, 0x1b, + 0x6b, 0x47, 0xe0, 0x9d, 0x15, 0xc3, 0x60, 0x75, + 0xd8, 0x49, 0x04, 0xf0, 0x81, 0x1d, 0xe2, 0x8d, + 0x05, 0xe0, 0xe2, 0x63, 0x81, 0x1d, 0xdd, 0x47, + 0xe2, 0x8b, 0x0b, 0xc3, 0x00, 0x1d, 0x61, 0x8d, + 0x3c, 0x03, 0x60, 0x75, 0xd8, 0x49, 0x06, 0xf1, + 0xdf, 0x48, 0x61, 0x95, 0x16, 0xe0, 0x4e, 0xe8, + 0x12, 0xe8, 0x21, 0xc5, 0xa0, 0x73, 0xb0, 0x49, + 0x03, 0xf0, 0x31, 0x48, 0xa0, 0x9b, 0x0d, 0xe0, + 0xc0, 0x49, 0x0b, 0xf1, 0xe2, 0x63, 0x7e, 0x1d, + 0xdd, 0x46, 0xe2, 0x8b, 0xe0, 0x75, 0x83, 0x1b, + 0xeb, 0x46, 0xfe, 0x1b, 0x6b, 0x46, 0xe0, 0x9d, + 0xe4, 0x49, 0x11, 0xf0, 0x10, 0x1d, 0xea, 0x8d, + 0xe3, 0x64, 0xc6, 0x49, 0x09, 0xf1, 0x07, 0xc5, + 0xa0, 0x73, 0xb1, 0x48, 0xa0, 0x9b, 0x02, 0xc5, + 0x00, 0xbd, 0xe6, 0x04, 0xa0, 0xd1, 0x02, 0xc5, + 0x00, 0xbd, 0xfe, 0x04, 0x02, 0xc5, 0x00, 0xbd, + 0x30, 0x05, 0x00, 0x00 }; + +static u16 r8152b_ram_code1[] = { + 0x9700, 0x7fe0, 0x4c00, 0x4007, 0x4400, 0x4800, 0x7c1f, 0x4c00, + 0x5310, 0x6000, 0x7c07, 0x6800, 0x673e, 0x0000, 0x0000, 0x571f, + 0x5ffb, 0xaa05, 0x5b58, 0x7d80, 0x6100, 0x3019, 0x5b64, 0x7d80, + 0x6080, 0xa6f8, 0xdcdb, 0x0015, 0xb915, 0xb511, 0xd16b, 0x000f, + 0xb40f, 0xd06b, 0x000d, 0xb206, 0x7c01, 0x5800, 0x7c04, 0x5c00, + 0x3011, 0x7c01, 0x5801, 0x7c04, 0x5c04, 0x3019, 0x30a5, 0x3127, + 0x31d5, 0x7fe0, 0x4c60, 0x7c07, 0x6803, 0x7d00, 0x6900, 0x65a0, + 0x0000, 0x0000, 0xaf03, 0x6015, 0x303e, 0x6017, 0x57e0, 0x580c, + 0x588c, 0x7fdd, 0x5fa2, 0x4827, 0x7c1f, 0x4c00, 0x7c1f, 0x4c10, + 0x8400, 0x7c30, 0x6020, 0x48bf, 0x7c1f, 0x4c00, 0x7c1f, 0x4c01, + 0x7c07, 0x6803, 0xb806, 0x7c08, 0x6800, 0x0000, 0x0000, 0x305c, + 0x7c08, 0x6808, 0x0000, 0x0000, 0xae06, 0x7c02, 0x5c02, 0x0000, + 0x0000, 0x3067, 0x8e05, 0x7c02, 0x5c00, 0x0000, 0x0000, 0xad06, + 0x7c20, 0x5c20, 0x0000, 0x0000, 0x3072, 0x8d05, 0x7c20, 0x5c00, + 0x0000, 0x0000, 0xa008, 0x7c07, 0x6800, 0xb8db, 0x7c07, 0x6803, + 0xd9b3, 0x00d7, 0x7fe0, 0x4c80, 0x7c08, 0x6800, 0x0000, 0x0000, + 0x7c23, 0x5c23, 0x481d, 0x7c1f, 0x4c00, 0x7c1f, 0x4c02, 0x5310, + 0x81ff, 0x30f5, 0x7fe0, 0x4d00, 0x4832, 0x7c1f, 0x4c00, 0x7c1f, + 0x4c10, 0x7c08, 0x6000, 0xa49e, 0x7c07, 0x6800, 0xb89b, 0x7c07, + 0x6803, 0xd9b3, 0x00f9, 0x7fe0, 0x4d20, 0x7e00, 0x6200, 0x3001, + 0x7fe0, 0x4dc0, 0xd09d, 0x0002, 0xb4fe, 0x7fe0, 0x4d80, 0x7c04, + 0x6004, 0x7c07, 0x6802, 0x6728, 0x0000, 0x0000, 0x7c08, 0x6000, + 0x486c, 0x7c1f, 0x4c00, 0x7c1f, 0x4c01, 0x9503, 0x7e00, 0x6200, + 0x571f, 0x5fbb, 0xaa05, 0x5b58, 0x7d80, 0x6100, 0x30c2, 0x5b64, + 0x7d80, 0x6080, 0xcdab, 0x0063, 0xcd8d, 0x0061, 0xd96b, 0x005f, + 0xd0a0, 0x00d7, 0xcba0, 0x0003, 0x80ec, 0x30cf, 0x30dc, 0x7fe0, + 0x4ce0, 0x4832, 0x7c1f, 0x4c00, 0x7c1f, 0x4c08, 0x7c08, 0x6008, + 0x8300, 0xb902, 0x30a5, 0x308a, 0x7fe0, 0x4da0, 0x65a8, 0x0000, + 0x0000, 0x56a0, 0x590c, 0x7ffd, 0x5fa2, 0xae06, 0x7c02, 0x5c02, + 0x0000, 0x0000, 0x30f0, 0x8e05, 0x7c02, 0x5c00, 0x0000, 0x0000, + 0xcba4, 0x0004, 0xcd8d, 0x0002, 0x80f1, 0x7fe0, 0x4ca0, 0x7c08, + 0x6408, 0x0000, 0x0000, 0x7d00, 0x6800, 0xb603, 0x7c10, 0x6010, + 0x7d1f, 0x551f, 0x5fb3, 0xaa07, 0x7c80, 0x5800, 0x5b58, 0x7d80, + 0x6100, 0x310f, 0x7c80, 0x5800, 0x5b64, 0x7d80, 0x6080, 0x4827, + 0x7c1f, 0x4c00, 0x7c1f, 0x4c10, 0x8400, 0x7c10, 0x6000, 0x7fe0, + 0x4cc0, 0x5fbb, 0x4824, 0x7c1f, 0x4c00, 0x7c1f, 0x4c04, 0x8200, + 0x7ce0, 0x5400, 0x6728, 0x0000, 0x0000, 0x30cf, 0x3001, 0x7fe0, + 0x4e00, 0x4007, 0x4400, 0x5310, 0x7c07, 0x6800, 0x673e, 0x0000, + 0x0000, 0x570f, 0x5fff, 0xaa05, 0x585b, 0x7d80, 0x6100, 0x313b, + 0x5867, 0x7d80, 0x6080, 0x9403, 0x7e00, 0x6200, 0xcda3, 0x00e7, + 0xcd85, 0x00e5, 0xd96b, 0x00e3, 0x96e3, 0x7c07, 0x6800, 0x673e, + 0x0000, 0x0000, 0x7fe0, 0x4e20, 0x96db, 0x8b04, 0x7c08, 0x5008, + 0xab03, 0x7c08, 0x5000, 0x7c07, 0x6801, 0x677e, 0x0000, 0x0000, + 0xdb7c, 0x00ec, 0x0000, 0x7fe1, 0x4f40, 0x4837, 0x4418, 0x41c7, + 0x7fe0, 0x4e40, 0x7c40, 0x5400, 0x7c1f, 0x4c01, 0x7c1f, 0x4c01, + 0x8fbf, 0xd2a0, 0x004b, 0x9204, 0xa042, 0x3168, 0x3127, 0x7fe1, + 0x4f60, 0x489c, 0x4628, 0x7fe0, 0x4e60, 0x7e28, 0x4628, 0x7c40, + 0x5400, 0x7c01, 0x5800, 0x7c04, 0x5c00, 0x41e8, 0x7c1f, 0x4c01, + 0x7c1f, 0x4c01, 0x8fa5, 0xb241, 0xa02a, 0x3182, 0x7fe0, 0x4ea0, + 0x7c02, 0x4402, 0x4448, 0x4894, 0x7c1f, 0x4c01, 0x7c1f, 0x4c03, + 0x4824, 0x7c1f, 0x4c07, 0x41ef, 0x41ff, 0x4891, 0x7c1f, 0x4c07, + 0x7c1f, 0x4c17, 0x8400, 0x8ef8, 0x41c7, 0x8f8a, 0x92d5, 0xa10f, + 0xd480, 0x0008, 0xd580, 0x00b8, 0xa202, 0x319d, 0x7c04, 0x4404, + 0x319d, 0xd484, 0x00f3, 0xd484, 0x00f1, 0x3127, 0x7fe0, 0x4ee0, + 0x7c40, 0x5400, 0x4488, 0x41cf, 0x3127, 0x7fe0, 0x4ec0, 0x48f3, + 0x7c1f, 0x4c01, 0x7c1f, 0x4c09, 0x4508, 0x41c7, 0x8fb0, 0xd218, + 0x00ae, 0xd2a4, 0x009e, 0x31be, 0x7fe0, 0x4e80, 0x4832, 0x7c1f, + 0x4c01, 0x7c1f, 0x4c11, 0x4428, 0x7c40, 0x5440, 0x7c01, 0x5801, + 0x7c04, 0x5c04, 0x41e8, 0xa4b3, 0x31d3, 0x7fe0, 0x4f20, 0x7c07, + 0x6800, 0x673e, 0x0000, 0x0000, 0x570f, 0x5fff, 0xaa04, 0x585b, + 0x6100, 0x31e4, 0x5867, 0x6080, 0xbcf1, 0x3001 }; + +static u16 r8152b_pla_patch_a_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x170b, 0xfc2a, 0x01e1, 0xfc2c, 0x0989, + 0xfc2e, 0x1349, 0xfc30, 0x01b7, 0xfc32, 0x061d, 0xe422, 0x0020, + 0xe420, 0x0018, 0xfc34, 0x1785, 0xfc36, 0x047b }; + +static u8 r8152b_pla_patch_a2[] = { + 0x08, 0xe0, 0x1a, 0xe0, 0xf2, 0xe0, 0xfa, 0xe0, + 0x32, 0xe1, 0x34, 0xe1, 0x36, 0xe1, 0x38, 0xe1, + 0x2c, 0x75, 0xdc, 0x21, 0xbc, 0x25, 0x04, 0x13, + 0x0b, 0xf0, 0x03, 0x13, 0x09, 0xf0, 0x02, 0x13, + 0x07, 0xf0, 0x01, 0x13, 0x05, 0xf0, 0x08, 0x13, + 0x03, 0xf0, 0x04, 0xc3, 0x00, 0xbb, 0x03, 0xc3, + 0x00, 0xbb, 0xd2, 0x17, 0xbc, 0x17, 0x14, 0xc2, + 0x40, 0x73, 0xba, 0x48, 0x40, 0x9b, 0x11, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x17, 0xf0, 0xbf, 0x49, + 0x03, 0xf1, 0x09, 0xc5, 0x00, 0xbd, 0xb1, 0x49, + 0x11, 0xf0, 0xb1, 0x48, 0x40, 0x9b, 0x02, 0xc2, + 0x00, 0xba, 0x4e, 0x19, 0x00, 0xa0, 0x1e, 0xfc, + 0xbc, 0xc0, 0xf0, 0xc0, 0xde, 0xe8, 0x00, 0x80, + 0x00, 0x60, 0x2c, 0x75, 0xd4, 0x49, 0x12, 0xf1, + 0x29, 0xe0, 0xf8, 0xc2, 0x46, 0x71, 0xf7, 0xc2, + 0x40, 0x73, 0xbe, 0x49, 0x03, 0xf1, 0xf5, 0xc7, + 0x02, 0xe0, 0xf2, 0xc7, 0x4f, 0x30, 0x26, 0x62, + 0xa1, 0x49, 0xf0, 0xf1, 0x22, 0x72, 0xa0, 0x49, + 0xed, 0xf1, 0x25, 0x25, 0x18, 0x1f, 0x97, 0x30, + 0x91, 0x30, 0x36, 0x9a, 0x2c, 0x75, 0x32, 0xc3, + 0x60, 0x73, 0xb1, 0x49, 0x0d, 0xf1, 0xdc, 0x21, + 0xbc, 0x25, 0x27, 0xc6, 0xc0, 0x77, 0x04, 0x13, + 0x18, 0xf0, 0x03, 0x13, 0x19, 0xf0, 0x02, 0x13, + 0x1a, 0xf0, 0x01, 0x13, 0x1b, 0xf0, 0xd4, 0x49, + 0x03, 0xf1, 0x1c, 0xc5, 0x00, 0xbd, 0xcd, 0xc6, + 0xc6, 0x67, 0x2e, 0x75, 0xd7, 0x22, 0xdd, 0x26, + 0x05, 0x15, 0x1a, 0xf0, 0x14, 0xc6, 0x00, 0xbe, + 0x13, 0xc5, 0x00, 0xbd, 0x12, 0xc5, 0x00, 0xbd, + 0xf1, 0x49, 0xfb, 0xf1, 0xef, 0xe7, 0xf4, 0x49, + 0xfa, 0xf1, 0xec, 0xe7, 0xf3, 0x49, 0xf7, 0xf1, + 0xe9, 0xe7, 0xf2, 0x49, 0xf4, 0xf1, 0xe6, 0xe7, + 0xb6, 0xc0, 0xf6, 0x14, 0x36, 0x14, 0x62, 0x14, + 0x86, 0x15, 0xa0, 0xd1, 0x00, 0x00, 0xc0, 0x75, + 0xd0, 0x49, 0x46, 0xf0, 0x26, 0x72, 0xa7, 0x49, + 0x43, 0xf0, 0x22, 0x72, 0x25, 0x25, 0x20, 0x1f, + 0x97, 0x30, 0x91, 0x30, 0x40, 0x73, 0xf3, 0xc4, + 0x1c, 0x40, 0x04, 0xf0, 0xd7, 0x49, 0x05, 0xf1, + 0x37, 0xe0, 0x53, 0x48, 0xc0, 0x9d, 0x08, 0x02, + 0x40, 0x66, 0x64, 0x27, 0x06, 0x16, 0x30, 0xf1, + 0x46, 0x63, 0x3b, 0x13, 0x2d, 0xf1, 0x34, 0x9b, + 0x18, 0x1b, 0x93, 0x30, 0x2b, 0xc3, 0x10, 0x1c, + 0x2b, 0xe8, 0x01, 0x14, 0x25, 0xf1, 0x00, 0x1d, + 0x26, 0x1a, 0x8a, 0x30, 0x22, 0x73, 0xb5, 0x25, + 0x0e, 0x0b, 0x00, 0x1c, 0x2c, 0xe8, 0x1f, 0xc7, + 0x27, 0x40, 0x1a, 0xf1, 0x38, 0xe8, 0x32, 0x1f, + 0x8f, 0x30, 0x08, 0x1b, 0x24, 0xe8, 0x36, 0x72, + 0x46, 0x77, 0x00, 0x17, 0x0d, 0xf0, 0x13, 0xc3, + 0x1f, 0x40, 0x03, 0xf1, 0x00, 0x1f, 0x46, 0x9f, + 0x44, 0x77, 0x9f, 0x44, 0x5f, 0x44, 0x17, 0xe8, + 0x0a, 0xc7, 0x27, 0x40, 0x05, 0xf1, 0x02, 0xc3, + 0x00, 0xbb, 0x1c, 0x1b, 0xd2, 0x1a, 0xff, 0xc7, + 0x00, 0xbf, 0xb8, 0xcd, 0xff, 0xff, 0x02, 0x0c, + 0x54, 0xa5, 0xdc, 0xa5, 0x2f, 0x40, 0x05, 0xf1, + 0x00, 0x14, 0xfa, 0xf1, 0x01, 0x1c, 0x02, 0xe0, + 0x00, 0x1c, 0x80, 0xff, 0xb0, 0x49, 0x04, 0xf0, + 0x01, 0x0b, 0xd3, 0xa1, 0x03, 0xe0, 0x02, 0x0b, + 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, 0x02, 0x0b, + 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, 0x00, 0x13, + 0xfb, 0xf1, 0x80, 0xff, 0x22, 0x73, 0xb5, 0x25, + 0x18, 0x1e, 0xde, 0x30, 0xd9, 0x30, 0x64, 0x72, + 0x11, 0x1e, 0x68, 0x23, 0x16, 0x31, 0x80, 0xff, + 0x08, 0xc2, 0x40, 0x73, 0x3a, 0x48, 0x40, 0x9b, + 0x06, 0xff, 0x02, 0xc6, 0x00, 0xbe, 0x4e, 0x18, + 0x1e, 0xfc, 0x33, 0xc5, 0xa0, 0x74, 0xc0, 0x49, + 0x1f, 0xf0, 0x30, 0xc5, 0xa0, 0x73, 0x00, 0x13, + 0x04, 0xf1, 0xa2, 0x73, 0x00, 0x13, 0x14, 0xf0, + 0x28, 0xc5, 0xa0, 0x74, 0xc8, 0x49, 0x1b, 0xf1, + 0x26, 0xc5, 0xa0, 0x76, 0xa2, 0x74, 0x01, 0x06, + 0x20, 0x37, 0xa0, 0x9e, 0xa2, 0x9c, 0x1e, 0xc5, + 0xa2, 0x73, 0x23, 0x40, 0x10, 0xf8, 0x04, 0xf3, + 0xa0, 0x73, 0x33, 0x40, 0x0c, 0xf8, 0x15, 0xc5, + 0xa0, 0x74, 0x41, 0x48, 0xa0, 0x9c, 0x14, 0xc5, + 0xa0, 0x76, 0x62, 0x48, 0xe0, 0x48, 0xa0, 0x9e, + 0x10, 0xc6, 0x00, 0xbe, 0x0a, 0xc5, 0xa0, 0x74, + 0x48, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0x20, 0x1e, + 0xa0, 0x9e, 0xe5, 0x48, 0xa0, 0x9e, 0xf0, 0xe7, + 0xbc, 0xc0, 0xc8, 0xd2, 0xcc, 0xd2, 0x28, 0xe4, + 0x22, 0x02, 0xf0, 0xc0, 0x02, 0xc6, 0x00, 0xbe, + 0x00, 0x00, 0x02, 0xc6, 0x00, 0xbe, 0x00, 0x00, + 0x02, 0xc6, 0x00, 0xbe, 0x00, 0x00, 0x02, 0xc6, + 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static u16 r8152b_pla_patch_a2_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x17a5, 0xfc2a, 0x13ad, + 0xfc2c, 0x184d, 0xfc2e, 0x01e1 }; + +static u16 r8153_ram_code_a[] = { + 0xE86C, 0xA000, 0xB436, 0xB820, 0xB438, 0x0290, 0xB436, 0xA012, + 0xB438, 0x0000, 0xB436, 0xA014, 0xB438, 0x2c04, 0xB438, 0x2c18, + 0xB438, 0x2c45, 0xB438, 0x2c45, 0xB438, 0xd502, 0xB438, 0x8301, + 0xB438, 0x8306, 0xB438, 0xd500, 0xB438, 0x8208, 0xB438, 0xd501, + 0xB438, 0xe018, 0xB438, 0x0308, 0xB438, 0x60f2, 0xB438, 0x8404, + 0xB438, 0x607d, 0xB438, 0xc117, 0xB438, 0x2c16, 0xB438, 0xc116, + 0xB438, 0x2c16, 0xB438, 0x607d, 0xB438, 0xc117, 0xB438, 0xa404, + 0xB438, 0xd500, 0xB438, 0x0800, 0xB438, 0xd501, 0xB438, 0x62d2, + 0xB438, 0x615d, 0xB438, 0xc115, 0xB438, 0xa404, 0xB438, 0xc307, + 0xB438, 0xd502, 0xB438, 0x8301, 0xB438, 0x8306, 0xB438, 0xd500, + 0xB438, 0x8208, 0xB438, 0x2c42, 0xB438, 0xc114, 0xB438, 0x8404, + 0xB438, 0xc317, 0xB438, 0xd701, 0xB438, 0x435d, 0xB438, 0xd500, + 0xB438, 0xa208, 0xB438, 0xd502, 0xB438, 0xa306, 0xB438, 0xa301, + 0xB438, 0x2c42, 0xB438, 0x8404, 0xB438, 0x613d, 0xB438, 0xc115, + 0xB438, 0xc307, 0xB438, 0xd502, 0xB438, 0x8301, 0xB438, 0x8306, + 0xB438, 0xd500, 0xB438, 0x8208, 0xB438, 0x2c42, 0xB438, 0xc114, + 0xB438, 0xc317, 0xB438, 0xd701, 0xB438, 0x40dd, 0xB438, 0xd500, + 0xB438, 0xa208, 0xB438, 0xd502, 0xB438, 0xa306, 0xB438, 0xa301, + 0xB438, 0xd500, 0xB438, 0xd702, 0xB438, 0x0800, 0xB436, 0xA01A, + 0xB438, 0x0000, 0xB436, 0xA006, 0xB438, 0x0fff, 0xB436, 0xA004, + 0xB438, 0x0fff, 0xB436, 0xA002, 0xB438, 0x05a3, 0xB436, 0xA000, + 0xB438, 0x3591, 0xB436, 0xB820, 0xB438, 0x0210 }; + +static u8 r8153_usb_patch_c[] = { + 0x08, 0xe0, 0x0a, 0xe0, 0x14, 0xe0, 0x58, 0xe0, + 0x64, 0xe0, 0x79, 0xe0, 0xab, 0xe0, 0xb6, 0xe0, + 0x02, 0xc5, 0x00, 0xbd, 0x38, 0x3b, 0xdb, 0x49, + 0x04, 0xf1, 0x06, 0xc3, 0x00, 0xbb, 0x5a, 0x02, + 0x05, 0xc4, 0x03, 0xc3, 0x00, 0xbb, 0xa4, 0x04, + 0x7e, 0x02, 0x30, 0xd4, 0x65, 0xc6, 0x66, 0x61, + 0x92, 0x49, 0x12, 0xf1, 0x3e, 0xc0, 0x02, 0x61, + 0x97, 0x49, 0x05, 0xf0, 0x3c, 0xc0, 0x00, 0x61, + 0x90, 0x49, 0x0a, 0xf1, 0xca, 0x63, 0xb0, 0x49, + 0x09, 0xf1, 0xb1, 0x49, 0x05, 0xf0, 0x32, 0xc0, + 0x00, 0x71, 0x9e, 0x49, 0x03, 0xf1, 0xb0, 0x48, + 0x05, 0xe0, 0x30, 0x48, 0xda, 0x61, 0x10, 0x48, + 0xda, 0x89, 0x4a, 0xc6, 0xc0, 0x60, 0x85, 0x49, + 0x03, 0xf0, 0x31, 0x48, 0x04, 0xe0, 0xb1, 0x48, + 0xb2, 0x48, 0x0f, 0xe0, 0x30, 0x18, 0x1b, 0xc1, + 0x0f, 0xe8, 0x1a, 0xc6, 0xc7, 0x65, 0xd0, 0x49, + 0x05, 0xf0, 0x32, 0x48, 0x02, 0xc2, 0x00, 0xba, + 0x3e, 0x16, 0x02, 0xc2, 0x00, 0xba, 0x48, 0x16, + 0x02, 0xc2, 0x00, 0xba, 0x4a, 0x16, 0x02, 0xb4, + 0x09, 0xc2, 0x40, 0x99, 0x0e, 0x48, 0x42, 0x98, + 0x42, 0x70, 0x8e, 0x49, 0xfe, 0xf1, 0x02, 0xb0, + 0x80, 0xff, 0xc0, 0xd4, 0xe4, 0x40, 0x20, 0xd4, + 0xca, 0xcf, 0x00, 0xcf, 0x3c, 0xe4, 0x0c, 0xc0, + 0x00, 0x63, 0xb5, 0x49, 0x09, 0xc0, 0x30, 0x18, + 0x06, 0xc1, 0xea, 0xef, 0xf5, 0xc7, 0x02, 0xc0, + 0x00, 0xb8, 0xd0, 0x10, 0xe4, 0x4b, 0x00, 0xd8, + 0x14, 0xc3, 0x60, 0x61, 0x90, 0x49, 0x06, 0xf0, + 0x11, 0xc3, 0x70, 0x61, 0x12, 0x48, 0x70, 0x89, + 0x08, 0xe0, 0x0a, 0xc6, 0xd4, 0x61, 0x93, 0x48, + 0xd4, 0x89, 0x02, 0xc1, 0x00, 0xb9, 0x72, 0x17, + 0x02, 0xc1, 0x00, 0xb9, 0x9c, 0x15, 0x00, 0xd8, + 0xef, 0xcf, 0x20, 0xd4, 0x30, 0x18, 0xe7, 0xc1, + 0xcb, 0xef, 0x2b, 0xc5, 0xa0, 0x77, 0x00, 0x1c, + 0xa0, 0x9c, 0x28, 0xc5, 0xa0, 0x64, 0xc0, 0x48, + 0xc1, 0x48, 0xc2, 0x48, 0xa0, 0x8c, 0xb1, 0x64, + 0xc0, 0x48, 0xb1, 0x8c, 0x20, 0xc5, 0xa0, 0x64, + 0x40, 0x48, 0x41, 0x48, 0xc2, 0x48, 0xa0, 0x8c, + 0x19, 0xc5, 0xa4, 0x64, 0x44, 0x48, 0xa4, 0x8c, + 0xb1, 0x64, 0x40, 0x48, 0xb1, 0x8c, 0x14, 0xc4, + 0x80, 0x73, 0x13, 0xc4, 0x82, 0x9b, 0x11, 0x1b, + 0x80, 0x9b, 0x0c, 0xc5, 0xa0, 0x64, 0x40, 0x48, + 0x41, 0x48, 0x42, 0x48, 0xa0, 0x8c, 0x05, 0xc5, + 0xa0, 0x9f, 0x02, 0xc5, 0x00, 0xbd, 0x6c, 0x3a, + 0x1e, 0xfc, 0x10, 0xd8, 0x86, 0xd4, 0xf8, 0xcb, + 0x20, 0xe4, 0x0a, 0xc0, 0x16, 0x61, 0x91, 0x48, + 0x16, 0x89, 0x07, 0xc0, 0x11, 0x19, 0x0c, 0x89, + 0x02, 0xc1, 0x00, 0xb9, 0x02, 0x06, 0x00, 0xd4, + 0x40, 0xb4, 0xfe, 0xc0, 0x16, 0x61, 0x91, 0x48, + 0x16, 0x89, 0xfb, 0xc0, 0x11, 0x19, 0x0c, 0x89, + 0x02, 0xc1, 0x00, 0xb9, 0xd2, 0x05, 0x00, 0x00 }; + +static u16 r8153_usb_patch_c_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x3b34, 0xfc2a, 0x027c, 0xfc2c, 0x15de, + 0xfc2e, 0x10ce, 0xfc30, 0x1adc, 0xfc32, 0x3a28, 0xfc34, 0x05f8, + 0xfc36, 0x05c8, 0xfc38, 0x00f3 }; + +static u8 r8153_pla_patch_c[] = { + 0x5d, 0xe0, 0x07, 0xe0, 0x0f, 0xe0, 0x5a, 0xe0, + 0x59, 0xe0, 0x1f, 0xe0, 0x57, 0xe0, 0x3e, 0xe1, + 0x08, 0xc2, 0x40, 0x73, 0x3a, 0x48, 0x40, 0x9b, + 0x06, 0xff, 0x02, 0xc6, 0x00, 0xbe, 0xcc, 0x17, + 0x1e, 0xfc, 0x2c, 0x75, 0xdc, 0x21, 0xbc, 0x25, + 0x04, 0x13, 0x0b, 0xf0, 0x03, 0x13, 0x09, 0xf0, + 0x02, 0x13, 0x07, 0xf0, 0x01, 0x13, 0x05, 0xf0, + 0x08, 0x13, 0x03, 0xf0, 0x04, 0xc3, 0x00, 0xbb, + 0x03, 0xc3, 0x00, 0xbb, 0x50, 0x17, 0x3a, 0x17, + 0x33, 0xc5, 0xa0, 0x74, 0xc0, 0x49, 0x1f, 0xf0, + 0x30, 0xc5, 0xa0, 0x73, 0x00, 0x13, 0x04, 0xf1, + 0xa2, 0x73, 0x00, 0x13, 0x14, 0xf0, 0x28, 0xc5, + 0xa0, 0x74, 0xc8, 0x49, 0x1b, 0xf1, 0x26, 0xc5, + 0xa0, 0x76, 0xa2, 0x74, 0x01, 0x06, 0x20, 0x37, + 0xa0, 0x9e, 0xa2, 0x9c, 0x1e, 0xc5, 0xa2, 0x73, + 0x23, 0x40, 0x10, 0xf8, 0x04, 0xf3, 0xa0, 0x73, + 0x33, 0x40, 0x0c, 0xf8, 0x15, 0xc5, 0xa0, 0x74, + 0x41, 0x48, 0xa0, 0x9c, 0x14, 0xc5, 0xa0, 0x76, + 0x62, 0x48, 0xe0, 0x48, 0xa0, 0x9e, 0x10, 0xc6, + 0x00, 0xbe, 0x0a, 0xc5, 0xa0, 0x74, 0x48, 0x48, + 0xa0, 0x9c, 0x0b, 0xc5, 0x20, 0x1e, 0xa0, 0x9e, + 0xe5, 0x48, 0xa0, 0x9e, 0xf0, 0xe7, 0xbc, 0xc0, + 0xc8, 0xd2, 0xcc, 0xd2, 0x28, 0xe4, 0xfa, 0x01, + 0xf0, 0xc0, 0x18, 0x89, 0x74, 0xc0, 0xcd, 0xe8, + 0x80, 0x76, 0x00, 0x1d, 0x6e, 0xc3, 0x66, 0x62, + 0xa0, 0x49, 0x06, 0xf0, 0x64, 0xc0, 0x02, 0x71, + 0x60, 0x99, 0x62, 0xc1, 0x03, 0xe0, 0x5f, 0xc0, + 0x60, 0xc1, 0x02, 0x99, 0x00, 0x61, 0x0f, 0x1b, + 0x59, 0x41, 0x03, 0x13, 0x18, 0xf1, 0xe4, 0x49, + 0x20, 0xf1, 0xe5, 0x49, 0x1e, 0xf0, 0x59, 0xc6, + 0xd0, 0x73, 0xb7, 0x49, 0x08, 0xf0, 0x01, 0x0b, + 0x80, 0x13, 0x03, 0xf0, 0xd0, 0x8b, 0x03, 0xe0, + 0x3f, 0x48, 0xd0, 0x9b, 0x51, 0xc0, 0x10, 0x1a, + 0x84, 0x1b, 0xb1, 0xe8, 0x4b, 0xc2, 0x40, 0x63, + 0x30, 0x48, 0x0a, 0xe0, 0xe5, 0x49, 0x09, 0xf0, + 0x47, 0xc0, 0x00, 0x1a, 0x84, 0x1b, 0xa7, 0xe8, + 0x41, 0xc2, 0x40, 0x63, 0xb0, 0x48, 0x40, 0x8b, + 0x67, 0x11, 0x3f, 0xf1, 0x69, 0x33, 0x32, 0xc0, + 0x28, 0x40, 0xd2, 0xf1, 0x33, 0xc0, 0x00, 0x19, + 0x81, 0x1b, 0x99, 0xe8, 0x30, 0xc0, 0x04, 0x1a, + 0x84, 0x1b, 0x95, 0xe8, 0x8a, 0xe8, 0xa3, 0x49, + 0xfe, 0xf0, 0x2a, 0xc0, 0x86, 0xe8, 0xa1, 0x48, + 0x84, 0x1b, 0x8d, 0xe8, 0x00, 0x1d, 0x69, 0x33, + 0x00, 0x1e, 0x01, 0x06, 0xff, 0x18, 0x30, 0x40, + 0xfd, 0xf1, 0x1f, 0xc0, 0x00, 0x76, 0x2e, 0x40, + 0xf7, 0xf1, 0x21, 0x48, 0x19, 0xc0, 0x84, 0x1b, + 0x7e, 0xe8, 0x74, 0x08, 0x72, 0xe8, 0xa1, 0x49, + 0xfd, 0xf0, 0x11, 0xc0, 0x00, 0x1a, 0x84, 0x1b, + 0x76, 0xe8, 0x6b, 0xe8, 0xa5, 0x49, 0xfe, 0xf0, + 0x09, 0xc0, 0x01, 0x19, 0x81, 0x1b, 0x6f, 0xe8, + 0x5a, 0xe0, 0xb8, 0x0b, 0x50, 0xe8, 0x83, 0x00, + 0x82, 0x00, 0x20, 0xb4, 0x10, 0xd8, 0x84, 0xd4, + 0x88, 0xd3, 0x10, 0xe0, 0x00, 0xd8, 0x24, 0xd4, + 0xf9, 0xc0, 0x57, 0xe8, 0x48, 0x33, 0xf3, 0xc0, + 0x00, 0x61, 0x6a, 0xc0, 0x47, 0x11, 0x03, 0xf0, + 0x57, 0x11, 0x05, 0xf1, 0x00, 0x61, 0x17, 0x48, + 0x00, 0x89, 0x41, 0xe0, 0x9c, 0x20, 0x9c, 0x24, + 0xd0, 0x49, 0x09, 0xf0, 0x04, 0x11, 0x07, 0xf1, + 0x00, 0x61, 0x97, 0x49, 0x38, 0xf0, 0x97, 0x48, + 0x00, 0x89, 0x2b, 0xe0, 0x00, 0x11, 0x05, 0xf1, + 0x00, 0x61, 0x92, 0x48, 0x00, 0x89, 0x2f, 0xe0, + 0x06, 0x11, 0x05, 0xf1, 0x00, 0x61, 0x11, 0x48, + 0x00, 0x89, 0x29, 0xe0, 0x05, 0x11, 0x0f, 0xf1, + 0x00, 0x61, 0x93, 0x49, 0x1a, 0xf1, 0x91, 0x49, + 0x0a, 0xf0, 0x91, 0x48, 0x00, 0x89, 0x0f, 0xe0, + 0xc6, 0xc0, 0x00, 0x61, 0x98, 0x20, 0x98, 0x24, + 0x25, 0x11, 0x80, 0xff, 0xfa, 0xef, 0x17, 0xf1, + 0x38, 0xc0, 0x1f, 0xe8, 0x95, 0x49, 0x13, 0xf0, + 0xf4, 0xef, 0x11, 0xf1, 0x31, 0xc0, 0x00, 0x61, + 0x92, 0x49, 0x0d, 0xf1, 0x12, 0x48, 0x00, 0x89, + 0x29, 0xc0, 0x00, 0x19, 0x00, 0x89, 0x27, 0xc0, + 0x01, 0x89, 0x23, 0xc0, 0x0e, 0xe8, 0x12, 0x48, + 0x81, 0x1b, 0x15, 0xe8, 0xae, 0xc3, 0x66, 0x62, + 0xa0, 0x49, 0x04, 0xf0, 0x64, 0x71, 0xa3, 0xc0, + 0x02, 0x99, 0x02, 0xc0, 0x00, 0xb8, 0xd6, 0x07, + 0x13, 0xc4, 0x84, 0x98, 0x00, 0x1b, 0x86, 0x8b, + 0x86, 0x73, 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0x71, + 0x82, 0x72, 0x80, 0xff, 0x09, 0xc4, 0x84, 0x98, + 0x80, 0x99, 0x82, 0x9a, 0x86, 0x8b, 0x86, 0x73, + 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0xff, 0x08, 0xea, + 0x30, 0xd4, 0x10, 0xc0, 0x12, 0xe8, 0x8a, 0xd3, + 0x00, 0xd8, 0x02, 0xc6, 0x00, 0xbe, 0xe0, 0x08 }; + +static u16 r8153_pla_patch_c_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x1306, 0xfc2a, 0x17ca, 0xfc2c, 0x171e, + 0xfc2e, 0x0000, 0xfc30, 0x0000, 0xfc32, 0x01b4, 0xfc34, 0x07d4, + 0xfc36, 0x0894, 0xfc38, 0x00e6 }; + +static u16 r8153_ram_code_bc[] = { + 0xB436, 0xB820, 0xB438, 0x0290, 0xB436, 0xA012, 0xB438, 0x0000, + 0xB436, 0xA014, 0xB438, 0x2c04, 0xB438, 0x2c07, 0xB438, 0x2c0a, + 0xB438, 0x2c0d, 0xB438, 0xa240, 0xB438, 0xa104, 0xB438, 0x292d, + 0xB438, 0x8620, 0xB438, 0xa480, 0xB438, 0x2a2c, 0xB438, 0x8480, + 0xB438, 0xa101, 0xB438, 0x2a36, 0xB438, 0xd056, 0xB438, 0x2223, + 0xB436, 0xA01A, 0xB438, 0x0000, 0xB436, 0xA006, 0xB438, 0x0222, + 0xB436, 0xA004, 0xB438, 0x0a35, 0xB436, 0xA002, 0xB438, 0x0a2b, + 0xB436, 0xA000, 0xB438, 0xf92c, 0xB436, 0xB820, 0xB438, 0x0210 }; + +static u8 r8153_usb_patch_b[] = { + 0x08, 0xe0, 0x0f, 0xe0, 0x18, 0xe0, 0x24, 0xe0, + 0x26, 0xe0, 0x3a, 0xe0, 0x84, 0xe0, 0x9c, 0xe0, + 0xc2, 0x49, 0x04, 0xf0, 0x02, 0xc0, 0x00, 0xb8, + 0x14, 0x18, 0x02, 0xc0, 0x00, 0xb8, 0x2e, 0x18, + 0x06, 0x89, 0x08, 0xc0, 0x0c, 0x61, 0x92, 0x48, + 0x93, 0x48, 0x0c, 0x89, 0x02, 0xc0, 0x00, 0xb8, + 0x08, 0x05, 0x40, 0xb4, 0x16, 0x89, 0x6d, 0xc0, + 0x00, 0x61, 0x95, 0x49, 0x06, 0xf0, 0xfa, 0xc0, + 0x0c, 0x61, 0x92, 0x48, 0x93, 0x48, 0x0c, 0x89, + 0x02, 0xc0, 0x00, 0xb8, 0xe2, 0x04, 0x02, 0xc2, + 0x00, 0xba, 0xec, 0x11, 0x60, 0x60, 0x85, 0x49, + 0x0d, 0xf1, 0x11, 0xc6, 0xd2, 0x61, 0x91, 0x49, + 0xfd, 0xf0, 0x74, 0x60, 0x04, 0x48, 0x74, 0x88, + 0x08, 0xc6, 0x08, 0xc0, 0xc4, 0x98, 0x01, 0x18, + 0xc0, 0x88, 0x02, 0xc0, 0x00, 0xb8, 0x6e, 0x12, + 0x04, 0xe4, 0x0d, 0x00, 0x00, 0xd4, 0xd1, 0x49, + 0x3c, 0xf1, 0xd2, 0x49, 0x16, 0xf1, 0xd3, 0x49, + 0x18, 0xf1, 0xd4, 0x49, 0x19, 0xf1, 0xd5, 0x49, + 0x1a, 0xf1, 0xd6, 0x49, 0x1b, 0xf1, 0xd7, 0x49, + 0x1c, 0xf1, 0xd8, 0x49, 0x1d, 0xf1, 0xd9, 0x49, + 0x20, 0xf1, 0xda, 0x49, 0x23, 0xf1, 0xdb, 0x49, + 0x24, 0xf1, 0x02, 0xc4, 0x00, 0xbc, 0x20, 0x04, + 0xe5, 0x8e, 0x02, 0xc4, 0x00, 0xbc, 0x14, 0x02, + 0x02, 0xc4, 0x00, 0xbc, 0x16, 0x02, 0x02, 0xc4, + 0x00, 0xbc, 0x18, 0x02, 0x02, 0xc4, 0x00, 0xbc, + 0x1a, 0x02, 0x02, 0xc4, 0x00, 0xbc, 0x1c, 0x02, + 0x02, 0xc4, 0x00, 0xbc, 0x94, 0x02, 0x10, 0xc7, + 0xe0, 0x8e, 0x02, 0xc4, 0x00, 0xbc, 0x8a, 0x02, + 0x0b, 0xc7, 0xe4, 0x8e, 0x02, 0xc4, 0x00, 0xbc, + 0x88, 0x02, 0x02, 0xc4, 0x00, 0xbc, 0x6e, 0x02, + 0x02, 0xc4, 0x00, 0xbc, 0x5a, 0x02, 0x30, 0xe4, + 0x0c, 0xc3, 0x60, 0x64, 0xc5, 0x49, 0x04, 0xf1, + 0x74, 0x64, 0xc4, 0x48, 0x74, 0x8c, 0x06, 0xc3, + 0x64, 0x8e, 0x02, 0xc4, 0x00, 0xbc, 0x20, 0x04, + 0x00, 0xd8, 0x00, 0xe4, 0xb2, 0xc0, 0x00, 0x61, + 0x90, 0x49, 0x09, 0xf1, 0x8b, 0xc6, 0xca, 0x61, + 0x94, 0x49, 0x0e, 0xf1, 0xf6, 0xc6, 0xda, 0x60, + 0x81, 0x49, 0x0a, 0xf0, 0x65, 0x60, 0x03, 0x48, + 0x65, 0x88, 0xef, 0xc6, 0xdc, 0x60, 0x80, 0x48, + 0xdc, 0x88, 0x05, 0xc6, 0x00, 0xbe, 0x02, 0xc6, + 0x00, 0xbe, 0x36, 0x13, 0x4c, 0x17, 0x99, 0xc4, + 0x80, 0x65, 0xd0, 0x49, 0x04, 0xf1, 0xfa, 0x75, + 0x04, 0xc4, 0x00, 0xbc, 0x03, 0xc4, 0x00, 0xbc, + 0x9a, 0x00, 0xee, 0x01 }; + +static u16 r8153_usb_patch_b_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x180c, 0xfc2a, 0x0506, 0xfc2c, 0x04E0, + 0xfc2e, 0x11E4, 0xfc30, 0x125C, 0xfc32, 0x0232, 0xfc34, 0x131E, + 0xfc36, 0x0098, 0xfc38, 0x00FF }; + +static u8 r8153_pla_patch_b[] = { + 0x08, 0xe0, 0xea, 0xe0, 0xf2, 0xe0, 0x04, 0xe1, + 0x09, 0xe1, 0x0e, 0xe1, 0x46, 0xe1, 0xf7, 0xe1, + 0x14, 0xc2, 0x40, 0x73, 0xba, 0x48, 0x40, 0x9b, + 0x11, 0xc2, 0x40, 0x73, 0xb0, 0x49, 0x17, 0xf0, + 0xbf, 0x49, 0x03, 0xf1, 0x09, 0xc5, 0x00, 0xbd, + 0xb1, 0x49, 0x11, 0xf0, 0xb1, 0x48, 0x40, 0x9b, + 0x02, 0xc2, 0x00, 0xba, 0x1a, 0x17, 0x00, 0xe0, + 0x1e, 0xfc, 0xbc, 0xc0, 0xf0, 0xc0, 0xde, 0xe8, + 0x00, 0x80, 0x00, 0x20, 0x2c, 0x75, 0xd4, 0x49, + 0x12, 0xf1, 0x32, 0xe0, 0xf8, 0xc2, 0x46, 0x71, + 0xf7, 0xc2, 0x40, 0x73, 0xbe, 0x49, 0x03, 0xf1, + 0xf5, 0xc7, 0x02, 0xe0, 0xf2, 0xc7, 0x4f, 0x30, + 0x26, 0x62, 0xa1, 0x49, 0xf0, 0xf1, 0x22, 0x72, + 0xa0, 0x49, 0xed, 0xf1, 0x25, 0x25, 0x18, 0x1f, + 0x97, 0x30, 0x91, 0x30, 0x36, 0x9a, 0x2c, 0x75, + 0x3c, 0xc3, 0x60, 0x73, 0xb1, 0x49, 0x0d, 0xf1, + 0xdc, 0x21, 0xbc, 0x25, 0x30, 0xc6, 0xc0, 0x77, + 0x04, 0x13, 0x21, 0xf0, 0x03, 0x13, 0x22, 0xf0, + 0x02, 0x13, 0x23, 0xf0, 0x01, 0x13, 0x24, 0xf0, + 0x08, 0x13, 0x08, 0xf1, 0x2e, 0x73, 0xba, 0x21, + 0xbd, 0x25, 0x05, 0x13, 0x03, 0xf1, 0x24, 0xc5, + 0x00, 0xbd, 0xd4, 0x49, 0x03, 0xf1, 0x1c, 0xc5, + 0x00, 0xbd, 0xc4, 0xc6, 0xc6, 0x67, 0x2e, 0x75, + 0xd7, 0x22, 0xdd, 0x26, 0x05, 0x15, 0x1b, 0xf0, + 0x14, 0xc6, 0x00, 0xbe, 0x13, 0xc5, 0x00, 0xbd, + 0x12, 0xc5, 0x00, 0xbd, 0xf1, 0x49, 0xfb, 0xf1, + 0xef, 0xe7, 0xf4, 0x49, 0xfa, 0xf1, 0xec, 0xe7, + 0xf3, 0x49, 0xf7, 0xf1, 0xe9, 0xe7, 0xf2, 0x49, + 0xf4, 0xf1, 0xe6, 0xe7, 0xb6, 0xc0, 0x9e, 0x12, + 0xde, 0x11, 0x0a, 0x12, 0x3c, 0x13, 0x00, 0xa0, + 0xa0, 0xd1, 0x00, 0x00, 0xc0, 0x75, 0xd0, 0x49, + 0x46, 0xf0, 0x26, 0x72, 0xa7, 0x49, 0x43, 0xf0, + 0x22, 0x72, 0x25, 0x25, 0x20, 0x1f, 0x97, 0x30, + 0x91, 0x30, 0x40, 0x73, 0xf3, 0xc4, 0x1c, 0x40, + 0x04, 0xf0, 0xd7, 0x49, 0x05, 0xf1, 0x37, 0xe0, + 0x53, 0x48, 0xc0, 0x9d, 0x08, 0x02, 0x40, 0x66, + 0x64, 0x27, 0x06, 0x16, 0x30, 0xf1, 0x46, 0x63, + 0x3b, 0x13, 0x2d, 0xf1, 0x34, 0x9b, 0x18, 0x1b, + 0x93, 0x30, 0x2b, 0xc3, 0x10, 0x1c, 0x2b, 0xe8, + 0x01, 0x14, 0x25, 0xf1, 0x00, 0x1d, 0x26, 0x1a, + 0x8a, 0x30, 0x22, 0x73, 0xb5, 0x25, 0x0e, 0x0b, + 0x00, 0x1c, 0x2c, 0xe8, 0x1f, 0xc7, 0x27, 0x40, + 0x1a, 0xf1, 0x38, 0xe8, 0x32, 0x1f, 0x8f, 0x30, + 0x08, 0x1b, 0x24, 0xe8, 0x36, 0x72, 0x46, 0x77, + 0x00, 0x17, 0x0d, 0xf0, 0x13, 0xc3, 0x1f, 0x40, + 0x03, 0xf1, 0x00, 0x1f, 0x46, 0x9f, 0x44, 0x77, + 0x9f, 0x44, 0x5f, 0x44, 0x17, 0xe8, 0x0a, 0xc7, + 0x27, 0x40, 0x05, 0xf1, 0x02, 0xc3, 0x00, 0xbb, + 0xfa, 0x18, 0xb0, 0x18, 0xff, 0xc7, 0x00, 0xbf, + 0xb8, 0xcd, 0xff, 0xff, 0x02, 0x0c, 0x54, 0xa5, + 0xdc, 0xa5, 0x2f, 0x40, 0x05, 0xf1, 0x00, 0x14, + 0xfa, 0xf1, 0x01, 0x1c, 0x02, 0xe0, 0x00, 0x1c, + 0x80, 0xff, 0xb0, 0x49, 0x04, 0xf0, 0x01, 0x0b, + 0xd3, 0xa1, 0x03, 0xe0, 0x02, 0x0b, 0xd3, 0xa5, + 0x27, 0x31, 0x20, 0x37, 0x02, 0x0b, 0xd3, 0xa5, + 0x27, 0x31, 0x20, 0x37, 0x00, 0x13, 0xfb, 0xf1, + 0x80, 0xff, 0x22, 0x73, 0xb5, 0x25, 0x18, 0x1e, + 0xde, 0x30, 0xd9, 0x30, 0x64, 0x72, 0x11, 0x1e, + 0x68, 0x23, 0x16, 0x31, 0x80, 0xff, 0x08, 0xc2, + 0x40, 0x73, 0x3a, 0x48, 0x40, 0x9b, 0x06, 0xff, + 0x02, 0xc6, 0x00, 0xbe, 0x08, 0x16, 0x1e, 0xfc, + 0x2c, 0x75, 0xdc, 0x21, 0xbc, 0x25, 0x04, 0x13, + 0x0b, 0xf0, 0x03, 0x13, 0x09, 0xf0, 0x02, 0x13, + 0x07, 0xf0, 0x01, 0x13, 0x05, 0xf0, 0x08, 0x13, + 0x03, 0xf0, 0x04, 0xc3, 0x00, 0xbb, 0x03, 0xc3, + 0x00, 0xbb, 0x8c, 0x15, 0x76, 0x15, 0xa0, 0x64, + 0x40, 0x48, 0xa0, 0x8c, 0x02, 0xc4, 0x00, 0xbc, + 0x82, 0x00, 0xa0, 0x62, 0x21, 0x48, 0xa0, 0x8a, + 0x02, 0xc2, 0x00, 0xba, 0x40, 0x03, 0x33, 0xc5, + 0xa0, 0x74, 0xc0, 0x49, 0x1f, 0xf0, 0x30, 0xc5, + 0xa0, 0x73, 0x00, 0x13, 0x04, 0xf1, 0xa2, 0x73, + 0x00, 0x13, 0x14, 0xf0, 0x28, 0xc5, 0xa0, 0x74, + 0xc8, 0x49, 0x1b, 0xf1, 0x26, 0xc5, 0xa0, 0x76, + 0xa2, 0x74, 0x01, 0x06, 0x20, 0x37, 0xa0, 0x9e, + 0xa2, 0x9c, 0x1e, 0xc5, 0xa2, 0x73, 0x23, 0x40, + 0x10, 0xf8, 0x04, 0xf3, 0xa0, 0x73, 0x33, 0x40, + 0x0c, 0xf8, 0x15, 0xc5, 0xa0, 0x74, 0x41, 0x48, + 0xa0, 0x9c, 0x14, 0xc5, 0xa0, 0x76, 0x62, 0x48, + 0xe0, 0x48, 0xa0, 0x9e, 0x10, 0xc6, 0x00, 0xbe, + 0x0a, 0xc5, 0xa0, 0x74, 0x48, 0x48, 0xa0, 0x9c, + 0x0b, 0xc5, 0x20, 0x1e, 0xa0, 0x9e, 0xe5, 0x48, + 0xa0, 0x9e, 0xf0, 0xe7, 0xbc, 0xc0, 0xc8, 0xd2, + 0xcc, 0xd2, 0x28, 0xe4, 0xe6, 0x01, 0xf0, 0xc0, + 0x18, 0x89, 0x00, 0x1d, 0x3c, 0xc3, 0x64, 0x71, + 0x3c, 0xc0, 0x02, 0x99, 0x00, 0x61, 0x67, 0x11, + 0x3c, 0xf1, 0x69, 0x33, 0x35, 0xc0, 0x28, 0x40, + 0xf6, 0xf1, 0x34, 0xc0, 0x00, 0x19, 0x81, 0x1b, + 0x91, 0xe8, 0x31, 0xc0, 0x04, 0x1a, 0x84, 0x1b, + 0x8d, 0xe8, 0x82, 0xe8, 0xa3, 0x49, 0xfe, 0xf0, + 0x2b, 0xc0, 0x7e, 0xe8, 0xa1, 0x48, 0x28, 0xc0, + 0x84, 0x1b, 0x84, 0xe8, 0x00, 0x1d, 0x69, 0x33, + 0x00, 0x1e, 0x01, 0x06, 0xff, 0x18, 0x30, 0x40, + 0xfd, 0xf1, 0x19, 0xc0, 0x00, 0x76, 0x2e, 0x40, + 0xf7, 0xf1, 0x21, 0x48, 0x19, 0xc0, 0x84, 0x1b, + 0x75, 0xe8, 0x10, 0xc0, 0x69, 0xe8, 0xa1, 0x49, + 0xfd, 0xf0, 0x11, 0xc0, 0x00, 0x1a, 0x84, 0x1b, + 0x6d, 0xe8, 0x62, 0xe8, 0xa5, 0x49, 0xfe, 0xf0, + 0x09, 0xc0, 0x01, 0x19, 0x81, 0x1b, 0x66, 0xe8, + 0x54, 0xe0, 0x10, 0xd4, 0x88, 0xd3, 0xb8, 0x0b, + 0x50, 0xe8, 0x20, 0xb4, 0x10, 0xd8, 0x84, 0xd4, + 0xfd, 0xc0, 0x52, 0xe8, 0x48, 0x33, 0xf9, 0xc0, + 0x00, 0x61, 0x9c, 0x20, 0x9c, 0x24, 0xd0, 0x49, + 0x04, 0xf0, 0x04, 0x11, 0x02, 0xf1, 0x03, 0xe0, + 0x00, 0x11, 0x06, 0xf1, 0x5c, 0xc0, 0x00, 0x61, + 0x92, 0x48, 0x00, 0x89, 0x3a, 0xe0, 0x06, 0x11, + 0x06, 0xf1, 0x55, 0xc0, 0x00, 0x61, 0x11, 0x48, + 0x00, 0x89, 0x33, 0xe0, 0x05, 0x11, 0x08, 0xf1, + 0x4e, 0xc0, 0x00, 0x61, 0x91, 0x49, 0x04, 0xf0, + 0x91, 0x48, 0x00, 0x89, 0x11, 0xe0, 0xd9, 0xc0, + 0x00, 0x61, 0x98, 0x20, 0x98, 0x24, 0x25, 0x11, + 0x24, 0xf1, 0x44, 0xc0, 0x29, 0xe8, 0x95, 0x49, + 0x20, 0xf0, 0xcf, 0xc0, 0x00, 0x61, 0x98, 0x20, + 0x98, 0x24, 0x25, 0x11, 0x1a, 0xf1, 0x37, 0xc0, + 0x00, 0x61, 0x92, 0x49, 0x16, 0xf1, 0x12, 0x48, + 0x00, 0x89, 0x2f, 0xc0, 0x00, 0x19, 0x00, 0x89, + 0x2d, 0xc0, 0x01, 0x89, 0x2d, 0xc0, 0x04, 0x19, + 0x81, 0x1b, 0x1c, 0xe8, 0x2a, 0xc0, 0x14, 0x19, + 0x81, 0x1b, 0x18, 0xe8, 0x21, 0xc0, 0x0c, 0xe8, + 0x1f, 0xc0, 0x12, 0x48, 0x81, 0x1b, 0x12, 0xe8, + 0xae, 0xc3, 0x66, 0x71, 0xae, 0xc0, 0x02, 0x99, + 0x02, 0xc0, 0x00, 0xb8, 0x96, 0x07, 0x13, 0xc4, + 0x84, 0x98, 0x00, 0x1b, 0x86, 0x8b, 0x86, 0x73, + 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0x71, 0x82, 0x72, + 0x80, 0xff, 0x09, 0xc4, 0x84, 0x98, 0x80, 0x99, + 0x82, 0x9a, 0x86, 0x8b, 0x86, 0x73, 0xbf, 0x49, + 0xfe, 0xf1, 0x80, 0xff, 0x08, 0xea, 0x30, 0xd4, + 0x10, 0xc0, 0x12, 0xe8, 0x8a, 0xd3, 0x28, 0xe4, + 0x2c, 0xe4, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00 }; + +static u16 r8153_pla_patch_b_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x1154, 0xfc2a, 0x1606, 0xfc2c, 0x155a, + 0xfc2e, 0x0080, 0xfc30, 0x033c, 0xfc32, 0x01a0, 0xfc34, 0x0794, + 0xfc36, 0x0000, 0xfc38, 0x007f }; + +static u16 r8153_ram_code_d[] = { + 0xB436, 0xB820, 0xB438, 0x0290, 0xB436, 0xA012, 0xB438, 0x0000, + 0xB436, 0xA014, 0xB438, 0x2c04, 0xB438, 0x2c07, 0xB438, 0x2c07, + 0xB438, 0x2c07, 0xB438, 0xa240, 0xB438, 0xa104, 0xB438, 0x2944, + 0xB436, 0xA01A, 0xB438, 0x0000, 0xB436, 0xA006, 0xB438, 0x0fff, + 0xB436, 0xA004, 0xB438, 0x0fff, 0xB436, 0xA002, 0xB438, 0x0fff, + 0xB436, 0xA000, 0xB438, 0x1943, 0xB436, 0xB820, 0xB438, 0x0210 }; + +static u8 usb_patch_d[] = { + 0x08, 0xe0, 0x0e, 0xe0, 0x11, 0xe0, 0x24, 0xe0, + 0x2b, 0xe0, 0x33, 0xe0, 0x3a, 0xe0, 0x3c, 0xe0, + 0x1e, 0xc3, 0x70, 0x61, 0x12, 0x48, 0x70, 0x89, + 0x02, 0xc3, 0x00, 0xbb, 0x02, 0x17, 0x32, 0x19, + 0x02, 0xc3, 0x00, 0xbb, 0x44, 0x14, 0x30, 0x18, + 0x11, 0xc1, 0x05, 0xe8, 0x10, 0xc6, 0x02, 0xc2, + 0x00, 0xba, 0x94, 0x17, 0x02, 0xb4, 0x09, 0xc2, + 0x40, 0x99, 0x0e, 0x48, 0x42, 0x98, 0x42, 0x70, + 0x8e, 0x49, 0xfe, 0xf1, 0x02, 0xb0, 0x80, 0xff, + 0xc0, 0xd4, 0xe4, 0x40, 0x20, 0xd4, 0x30, 0x18, + 0x06, 0xc1, 0xf1, 0xef, 0xfc, 0xc7, 0x02, 0xc0, + 0x00, 0xb8, 0x38, 0x12, 0xe4, 0x4b, 0x0c, 0x61, + 0x92, 0x48, 0x93, 0x48, 0x95, 0x48, 0x96, 0x48, + 0x0c, 0x89, 0x02, 0xc0, 0x00, 0xb8, 0x0e, 0x06, + 0x30, 0x18, 0xf5, 0xc1, 0xe0, 0xef, 0x04, 0xc5, + 0x02, 0xc4, 0x00, 0xbc, 0x76, 0x3c, 0x1e, 0xfc, + 0x02, 0xc6, 0x00, 0xbe, 0x00, 0x00, 0x02, 0xc6, + 0x00, 0xbe, 0x00, 0x00 }; + +static u16 r8153_usb_patch_d_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x16de, 0xfc2a, 0x1442, 0xfc2c, 0x1792, + 0xfc2e, 0x1236, 0xfc30, 0x0606, 0xfc32, 0x3c74, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x003e }; + +static u8 pla_patch_d[] = { + 0x03, 0xe0, 0x16, 0xe0, 0x30, 0xe0, 0x12, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x08, 0xf0, 0xb8, 0x49, + 0x06, 0xf0, 0xb8, 0x48, 0x40, 0x9b, 0x0b, 0xc2, + 0x40, 0x76, 0x05, 0xe0, 0x02, 0x61, 0x02, 0xc3, + 0x00, 0xbb, 0x54, 0x08, 0x02, 0xc3, 0x00, 0xbb, + 0x64, 0x08, 0x98, 0xd3, 0x1e, 0xfc, 0xfe, 0xc0, + 0x02, 0x62, 0xa0, 0x48, 0x02, 0x8a, 0x00, 0x72, + 0xa0, 0x49, 0x11, 0xf0, 0x13, 0xc1, 0x20, 0x62, + 0x2e, 0x21, 0x2f, 0x25, 0x00, 0x71, 0x9f, 0x24, + 0x0a, 0x40, 0x09, 0xf0, 0x00, 0x71, 0x18, 0x48, + 0xa0, 0x49, 0x03, 0xf1, 0x9f, 0x48, 0x02, 0xe0, + 0x1f, 0x48, 0x00, 0x99, 0x02, 0xc2, 0x00, 0xba, + 0xac, 0x0c, 0x08, 0xe9, 0x36, 0xc0, 0x00, 0x61, + 0x9c, 0x20, 0x9c, 0x24, 0x33, 0xc0, 0x07, 0x11, + 0x05, 0xf1, 0x00, 0x61, 0x17, 0x48, 0x00, 0x89, + 0x0d, 0xe0, 0x04, 0x11, 0x0b, 0xf1, 0x00, 0x61, + 0x97, 0x49, 0x08, 0xf0, 0x97, 0x48, 0x00, 0x89, + 0x23, 0xc0, 0x0e, 0xe8, 0x12, 0x48, 0x81, 0x1b, + 0x15, 0xe8, 0x1f, 0xc0, 0x00, 0x61, 0x67, 0x11, + 0x04, 0xf0, 0x02, 0xc0, 0x00, 0xb8, 0x42, 0x09, + 0x02, 0xc0, 0x00, 0xb8, 0x90, 0x08, 0x13, 0xc4, + 0x84, 0x98, 0x00, 0x1b, 0x86, 0x8b, 0x86, 0x73, + 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0x71, 0x82, 0x72, + 0x80, 0xff, 0x09, 0xc4, 0x84, 0x98, 0x80, 0x99, + 0x82, 0x9a, 0x86, 0x8b, 0x86, 0x73, 0xbf, 0x49, + 0xfe, 0xf1, 0x80, 0xff, 0x08, 0xea, 0x30, 0xd4, + 0x50, 0xe8, 0x8a, 0xd3 }; + +static u16 r8153_pla_patch_d_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x0852, 0xfc2a, 0x0c92, 0xfc2c, 0x088c, + 0xfc2e, 0x0000, 0xfc30, 0x0000, 0xfc32, 0x0000, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x0007 }; + +static u8 usb_patch2_b[] = { + 0x10, 0xe0, 0x26, 0xe0, 0x3a, 0xe0, 0x58, 0xe0, + 0x6c, 0xe0, 0x85, 0xe0, 0xa5, 0xe0, 0xbe, 0xe0, + 0xd8, 0xe0, 0xdb, 0xe0, 0xf3, 0xe0, 0xf5, 0xe0, + 0xf7, 0xe0, 0xf9, 0xe0, 0xfb, 0xe0, 0xfd, 0xe0, + 0x16, 0xc0, 0x00, 0x75, 0xd1, 0x49, 0x0d, 0xf0, + 0x0f, 0xc0, 0x0f, 0xc5, 0x00, 0x1e, 0x08, 0x9e, + 0x0c, 0x9d, 0x0c, 0xc6, 0x0a, 0x9e, 0x8f, 0x1c, + 0x0e, 0x8c, 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, + 0x02, 0xc0, 0x00, 0xb8, 0x96, 0x31, 0x00, 0xdc, + 0x24, 0xe4, 0x80, 0x02, 0x34, 0xd3, 0xff, 0xc3, + 0x60, 0x72, 0xa1, 0x49, 0x0d, 0xf0, 0xf8, 0xc3, + 0xf8, 0xc2, 0x00, 0x1c, 0x68, 0x9c, 0xf6, 0xc4, + 0x6a, 0x9c, 0x6c, 0x9a, 0x8f, 0x1c, 0x6e, 0x8c, + 0x6e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, 0x04, 0xc0, + 0x02, 0xc2, 0x00, 0xba, 0xa8, 0x28, 0xf8, 0xc7, + 0xea, 0xc0, 0x00, 0x75, 0xd1, 0x49, 0x15, 0xf0, + 0x19, 0xc7, 0x17, 0xc2, 0xec, 0x9a, 0x00, 0x19, + 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0xea, 0x71, 0x9f, 0x49, 0x0a, 0xf0, 0xd9, 0xc2, + 0xec, 0x9a, 0x00, 0x19, 0xe8, 0x99, 0x81, 0x19, + 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0x06, 0xc3, 0x02, 0xc2, 0x00, 0xba, 0xf0, 0x1d, + 0x4c, 0xe8, 0x00, 0xdc, 0x00, 0xd4, 0xcb, 0xc0, + 0x00, 0x75, 0xd1, 0x49, 0x0d, 0xf0, 0xc4, 0xc0, + 0xc4, 0xc5, 0x00, 0x1e, 0x08, 0x9e, 0xc2, 0xc6, + 0x0a, 0x9e, 0x0c, 0x9d, 0x8f, 0x1c, 0x0e, 0x8c, + 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, 0x04, 0xc0, + 0x02, 0xc1, 0x00, 0xb9, 0xc4, 0x16, 0x20, 0xd4, + 0xb6, 0xc0, 0x00, 0x75, 0xd1, 0x48, 0x00, 0x9d, + 0xe5, 0xc7, 0xaf, 0xc2, 0xec, 0x9a, 0x00, 0x19, + 0xe8, 0x9a, 0x81, 0x19, 0xee, 0x89, 0xee, 0x71, + 0x9f, 0x49, 0xfe, 0xf1, 0x2c, 0xc1, 0xec, 0x99, + 0x81, 0x19, 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, + 0xfe, 0xf1, 0x04, 0xc3, 0x02, 0xc2, 0x00, 0xba, + 0x96, 0x1c, 0xc0, 0xd4, 0xc0, 0x88, 0x1e, 0xc6, + 0xc0, 0x70, 0x8f, 0x49, 0x0e, 0xf0, 0x8f, 0x48, + 0x93, 0xc6, 0xca, 0x98, 0x11, 0x18, 0xc8, 0x98, + 0x16, 0xc0, 0xcc, 0x98, 0x8f, 0x18, 0xce, 0x88, + 0xce, 0x70, 0x8f, 0x49, 0xfe, 0xf1, 0x0b, 0xe0, + 0x43, 0xc6, 0x00, 0x18, 0xc8, 0x98, 0x0b, 0xc0, + 0xcc, 0x98, 0x81, 0x18, 0xce, 0x88, 0xce, 0x70, + 0x8f, 0x49, 0xfe, 0xf1, 0x02, 0xc0, 0x00, 0xb8, + 0xf2, 0x19, 0x40, 0xd3, 0x20, 0xe4, 0x33, 0xc2, + 0x40, 0x71, 0x91, 0x48, 0x40, 0x99, 0x30, 0xc2, + 0x00, 0x19, 0x48, 0x99, 0xf8, 0xc1, 0x4c, 0x99, + 0x81, 0x19, 0x4e, 0x89, 0x4e, 0x71, 0x9f, 0x49, + 0xfe, 0xf1, 0x0b, 0xc1, 0x4c, 0x99, 0x81, 0x19, + 0x4e, 0x89, 0x4e, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0x02, 0x71, 0x02, 0xc2, 0x00, 0xba, 0x0e, 0x34, + 0x24, 0xe4, 0x19, 0xc2, 0x40, 0x71, 0x91, 0x48, + 0x40, 0x99, 0x16, 0xc2, 0x00, 0x19, 0x48, 0x99, + 0xde, 0xc1, 0x4c, 0x99, 0x81, 0x19, 0x4e, 0x89, + 0x4e, 0x71, 0x9f, 0x49, 0xfe, 0xf1, 0xf1, 0xc1, + 0x4c, 0x99, 0x81, 0x19, 0x4e, 0x89, 0x4e, 0x71, + 0x9f, 0x49, 0xfe, 0xf1, 0x02, 0x71, 0x02, 0xc2, + 0x00, 0xba, 0x60, 0x33, 0x34, 0xd3, 0x00, 0xdc, + 0x1e, 0x89, 0x02, 0xc0, 0x00, 0xb8, 0xfa, 0x12, + 0x18, 0xc0, 0x00, 0x65, 0xd1, 0x49, 0x0e, 0xf0, + 0x11, 0xc0, 0x11, 0xc5, 0x00, 0x1e, 0x08, 0x9e, + 0x0c, 0x9d, 0x0e, 0xc6, 0x0a, 0x9e, 0x8f, 0x1c, + 0x0e, 0x8c, 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, + 0x04, 0xc0, 0x02, 0xc2, 0x00, 0xba, 0xa0, 0x41, + 0x06, 0xd4, 0x00, 0xdc, 0x24, 0xe4, 0x80, 0x02, + 0x34, 0xd3, 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, + 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, 0x02, 0xc0, + 0x00, 0xb8, 0x00, 0x00, 0x02, 0xc0, 0x00, 0xb8, + 0x00, 0x00, 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, + 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00 }; + +static u16 r8153b_usb_patch_b_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x2a20, 0xfc2a, 0x28a6, 0xfc2c, 0x1dee, + 0xfc2e, 0x16c2, 0xfc30, 0x1c94, 0xfc32, 0x19f0, 0xfc34, 0x340c, + 0xfc36, 0x335e, 0xfc38, 0x12f8, 0xfc3a, 0x419e, 0xfc3c, 0x0000, + 0xfc3e, 0x0000, 0xfc40, 0x0000, 0xfc42, 0x0000, 0xfc44, 0x0000, + 0xfc46, 0x0000, 0xfc48, 0x03ff }; + +static u8 pla_patch2_b[] = { + 0x05, 0xe0, 0x1b, 0xe0, 0x2c, 0xe0, 0x60, 0xe0, + 0x73, 0xe0, 0x15, 0xc6, 0xc2, 0x64, 0xd2, 0x49, + 0x06, 0xf1, 0xc4, 0x48, 0xc5, 0x48, 0xc6, 0x48, + 0xc7, 0x48, 0x05, 0xe0, 0x44, 0x48, 0x45, 0x48, + 0x46, 0x48, 0x47, 0x48, 0xc2, 0x8c, 0xc0, 0x64, + 0x46, 0x48, 0xc0, 0x8c, 0x05, 0xc5, 0x02, 0xc4, + 0x00, 0xbc, 0x18, 0x02, 0x06, 0xdc, 0xb0, 0xc0, + 0x10, 0xc5, 0xa0, 0x77, 0xa0, 0x74, 0x46, 0x48, + 0x47, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0xa0, 0x74, + 0x44, 0x48, 0x43, 0x48, 0xa0, 0x9c, 0x05, 0xc5, + 0xa0, 0x9f, 0x02, 0xc5, 0x00, 0xbd, 0x3c, 0x03, + 0x1c, 0xe8, 0x20, 0xe8, 0xd4, 0x49, 0x04, 0xf1, + 0xd5, 0x49, 0x20, 0xf1, 0x28, 0xe0, 0x2a, 0xc7, + 0xe0, 0x75, 0xda, 0x49, 0x14, 0xf0, 0x27, 0xc7, + 0xe0, 0x75, 0xdc, 0x49, 0x10, 0xf1, 0x24, 0xc7, + 0xe0, 0x75, 0x25, 0xc7, 0xe0, 0x74, 0x2c, 0x40, + 0x0a, 0xfa, 0x1f, 0xc7, 0xe4, 0x75, 0xd0, 0x49, + 0x09, 0xf1, 0x1c, 0xc5, 0xe6, 0x9d, 0x11, 0x1d, + 0xe4, 0x8d, 0x04, 0xe0, 0x16, 0xc7, 0x00, 0x1d, + 0xe4, 0x8d, 0xe0, 0x8e, 0x11, 0x1d, 0xe0, 0x8d, + 0x07, 0xe0, 0x0c, 0xc7, 0xe0, 0x75, 0xda, 0x48, + 0xe0, 0x9d, 0x0b, 0xc7, 0xe4, 0x8e, 0x02, 0xc4, + 0x00, 0xbc, 0x28, 0x03, 0x02, 0xc4, 0x00, 0xbc, + 0x14, 0x03, 0x12, 0xe8, 0x4e, 0xe8, 0x1c, 0xe6, + 0x20, 0xe4, 0x80, 0x02, 0xa4, 0xc0, 0x12, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x08, 0xf0, 0xb8, 0x49, + 0x06, 0xf0, 0xb8, 0x48, 0x40, 0x9b, 0x0b, 0xc2, + 0x40, 0x76, 0x05, 0xe0, 0x02, 0x61, 0x02, 0xc3, + 0x00, 0xbb, 0x0a, 0x0a, 0x02, 0xc3, 0x00, 0xbb, + 0x1a, 0x0a, 0x98, 0xd3, 0x1e, 0xfc, 0xfe, 0xc0, + 0x02, 0x62, 0xa0, 0x48, 0x02, 0x8a, 0x00, 0x72, + 0xa0, 0x49, 0x11, 0xf0, 0x13, 0xc1, 0x20, 0x62, + 0x2e, 0x21, 0x2f, 0x25, 0x00, 0x71, 0x9f, 0x24, + 0x0a, 0x40, 0x09, 0xf0, 0x00, 0x71, 0x18, 0x48, + 0xa0, 0x49, 0x03, 0xf1, 0x9f, 0x48, 0x02, 0xe0, + 0x1f, 0x48, 0x00, 0x99, 0x02, 0xc2, 0x00, 0xba, + 0xda, 0x0e, 0x08, 0xe9 }; + +static u16 r8153b_pla_patch_b_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x0216, 0xfc2a, 0x0332, 0xfc2c, 0x030c, + 0xfc2e, 0x0a08, 0xfc30, 0x0ec0, 0xfc32, 0x0000, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x001e }; + +static void rtl_clear_bp(struct r8152 *tp, u16 type) +{ + u8 zeros[16] = {0}; + + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + case RTL_VER_07: + break; + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ocp_write_byte(tp, type, PLA_BP_EN, 0); + break; + case RTL_VER_08: + case RTL_VER_09: + default: + if (type == MCU_TYPE_USB) { + ocp_write_byte(tp, MCU_TYPE_USB, USB_BP2_EN, 0); + + generic_ocp_write(tp, USB_BP(8), 0xff, sizeof(zeros), + zeros, type); + } else { + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0); + } + break; + } + + generic_ocp_write(tp, USB_BP(0), 0xff, sizeof(zeros), zeros, type); + + mdelay(6); + + ocp_write_word(tp, type, PLA_BP_BA, 0); +} + +static void r8152b_set_dq_desc(struct r8152 *tp) +{ + u8 data; + + data = ocp_read_byte(tp, MCU_TYPE_USB, 0xd429); + data |= 0x80; + ocp_write_byte(tp, MCU_TYPE_USB, 0xd429, data); + ocp_write_word(tp, MCU_TYPE_USB, 0xc0ce, 0x0210); + data = ocp_read_byte(tp, MCU_TYPE_USB, 0xd429); + data &= ~0x80; + ocp_write_byte(tp, MCU_TYPE_USB, 0xd429, data); +} + +static int r8153_pre_ram_code(struct r8152 *tp, u16 patch_key) +{ + u16 data; + int i; + + data = ocp_reg_read(tp, 0xb820); + data |= 0x0010; + ocp_reg_write(tp, 0xb820, data); + + for (i = 0, data = 0; !data && i < 5000; i++) { + mdelay(2); + data = ocp_reg_read(tp, 0xb800) & 0x0040; + } + + sram_write(tp, 0x8146, patch_key); + sram_write(tp, 0xb82e, 0x0001); + + return -EBUSY; +} + +static int r8153_post_ram_code(struct r8152 *tp) +{ + u16 data; + + sram_write(tp, 0x0000, 0x0000); + + data = ocp_reg_read(tp, 0xb82e); + data &= ~0x0001; + ocp_reg_write(tp, 0xb82e, data); + + sram_write(tp, 0x8146, 0x0000); + + data = ocp_reg_read(tp, 0xb820); + data &= ~0x0010; + ocp_reg_write(tp, 0xb820, data); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base); + + return 0; +} + +static void r8153_wdt1_end(struct r8152 *tp) +{ + int i; + + for (i = 0; i < 104; i++) { + if (!(ocp_read_byte(tp, MCU_TYPE_USB, 0xe404) & 1)) + break; + mdelay(2); + } +} + +void r8152b_firmware(struct r8152 *tp) +{ + int i; + + if (tp->version == RTL_VER_01) { + int i; + + r8152b_set_dq_desc(tp); + rtl_clear_bp(tp, MCU_TYPE_PLA); + + generic_ocp_write(tp, 0xf800, 0x3f, + sizeof(r8152b_pla_patch_a), + r8152b_pla_patch_a, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8152b_pla_patch_a_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8152b_pla_patch_a_bp[i], + r8152b_pla_patch_a_bp[i+1]); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, 0x2000); + ocp_write_word(tp, MCU_TYPE_PLA, 0xb092, 0x7070); + ocp_write_word(tp, MCU_TYPE_PLA, 0xb098, 0x0600); + for (i = 0; i < ARRAY_SIZE(r8152b_ram_code1); i++) + ocp_write_word(tp, MCU_TYPE_PLA, 0xb09a, + r8152b_ram_code1[i]); + + ocp_write_word(tp, MCU_TYPE_PLA, 0xb098, 0x0200); + ocp_write_word(tp, MCU_TYPE_PLA, 0xb092, 0x7030); + } else if (tp->version == RTL_VER_02) { + rtl_clear_bp(tp, MCU_TYPE_PLA); + + generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8152b_pla_patch_a2), + r8152b_pla_patch_a2, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8152b_pla_patch_a2_bp); + i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8152b_pla_patch_a2_bp[i], + r8152b_pla_patch_a2_bp[i+1]); + } +} + +void r8153_firmware(struct r8152 *tp) +{ + int i; + + if (tp->version == RTL_VER_03) { + r8153_pre_ram_code(tp, 0x7000); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_a); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_a[i], + r8153_ram_code_a[i+1]); + + r8153_post_ram_code(tp); + } else if (tp->version == RTL_VER_04) { + r8153_pre_ram_code(tp, 0x7001); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_bc); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_bc[i], + r8153_ram_code_bc[i+1]); + + r8153_post_ram_code(tp); + + r8153_wdt1_end(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); + + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_usb_patch_b), + r8153_usb_patch_b, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153_usb_patch_b_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_USB, + r8153_usb_patch_b_bp[i], + r8153_usb_patch_b_bp[i+1]); + + if (!(ocp_read_word(tp, MCU_TYPE_PLA, 0xd38e) & BIT(0))) { + ocp_write_word(tp, MCU_TYPE_PLA, 0xd38c, 0x0082); + ocp_write_word(tp, MCU_TYPE_PLA, 0xd38e, 0x0082); + } + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_pla_patch_b), + r8153_pla_patch_b, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153_pla_patch_b_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_pla_patch_b_bp[i], + r8153_pla_patch_b_bp[i+1]); + + ocp_write_word(tp, MCU_TYPE_PLA, 0xd388, 0x08ca); + } else if (tp->version == RTL_VER_05) { + u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, 0xcfca); + ocp_data &= ~0x4000; + ocp_write_word(tp, MCU_TYPE_USB, 0xcfca, ocp_data); + + r8153_pre_ram_code(tp, 0x7001); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_bc); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_bc[i], + r8153_ram_code_bc[i+1]); + + r8153_post_ram_code(tp); + + r8153_wdt1_end(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); + + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_usb_patch_c), + r8153_usb_patch_c, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153_usb_patch_c_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_USB, + r8153_usb_patch_c_bp[i], + r8153_usb_patch_c_bp[i+1]); + + if (ocp_read_byte(tp, MCU_TYPE_USB, 0xcfef) & 1) { + ocp_write_word(tp, MCU_TYPE_USB, 0xfc30, 0x1578); + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x00ff); + } else { + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x00ef); + } + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_pla_patch_c), + r8153_pla_patch_c, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153_pla_patch_c_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_pla_patch_c_bp[i], + r8153_pla_patch_c_bp[i+1]); + + ocp_write_word(tp, MCU_TYPE_PLA, 0xd388, 0x08ca); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, + U3P3_CHECK_EN | 4); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, 0xcfca); + ocp_data |= 0x4000; + ocp_write_word(tp, MCU_TYPE_USB, 0xcfca, ocp_data); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + } else if (tp->version == RTL_VER_06) { + u32 ocp_data; + + r8153_pre_ram_code(tp, 0x7002); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_d); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_d[i], + r8153_ram_code_d[i+1]); + + r8153_post_ram_code(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); + + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, sizeof(usb_patch_d), + usb_patch_d, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153_usb_patch_d_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_USB, + r8153_usb_patch_d_bp[i], + r8153_usb_patch_d_bp[i+1]); + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, sizeof(pla_patch_d), + pla_patch_d, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153_pla_patch_d_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153_pla_patch_d_bp[i], + r8153_pla_patch_d_bp[i + 1]); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1); + ocp_data |= FW_IP_RESET_EN; + ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data); + } +} + +void r8153b_firmware(struct r8152 *tp) +{ + u32 ocp_data; + int i; + + if (tp->version != RTL_VER_09) + return; + + rtl_clear_bp(tp, MCU_TYPE_USB); + + ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + generic_ocp_write(tp, 0xe600, 0xff, sizeof(usb_patch2_b), + usb_patch2_b, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153b_usb_patch_b_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_USB, + r8153b_usb_patch_b_bp[i], + r8153b_usb_patch_b_bp[i + 1]); + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + generic_ocp_write(tp, 0xf800, 0xff, sizeof(pla_patch2_b), + pla_patch2_b, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153b_pla_patch_b_bp); i += 2) + ocp_write_word(tp, MCU_TYPE_PLA, + r8153b_pla_patch_b_bp[i], + r8153b_pla_patch_b_bp[i + 1]); + + ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1); + ocp_data |= FW_IP_RESET_EN; + ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data); +} diff --git a/roms/u-boot/drivers/usb/eth/smsc95xx.c b/roms/u-boot/drivers/usb/eth/smsc95xx.c new file mode 100644 index 000000000..283c52c16 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/smsc95xx.c @@ -0,0 +1,1080 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (C) 2009 NVIDIA, Corporation + * Copyright (C) 2007-2008 SMSC (Steve Glendinning) + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <memalign.h> +#include <net.h> +#include <usb.h> +#include <asm/unaligned.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include "usb_ether.h" + +/* SMSC LAN95xx based USB 2.0 Ethernet Devices */ + +/* LED defines */ +#define LED_GPIO_CFG (0x24) +#define LED_GPIO_CFG_SPD_LED (0x01000000) +#define LED_GPIO_CFG_LNK_LED (0x00100000) +#define LED_GPIO_CFG_FDX_LED (0x00010000) + +/* Tx command words */ +#define TX_CMD_A_FIRST_SEG_ 0x00002000 +#define TX_CMD_A_LAST_SEG_ 0x00001000 + +/* Rx status word */ +#define RX_STS_FL_ 0x3FFF0000 /* Frame Length */ +#define RX_STS_ES_ 0x00008000 /* Error Summary */ + +/* SCSRs */ +#define ID_REV 0x00 + +#define INT_STS 0x08 + +#define TX_CFG 0x10 +#define TX_CFG_ON_ 0x00000004 + +#define HW_CFG 0x14 +#define HW_CFG_BIR_ 0x00001000 +#define HW_CFG_RXDOFF_ 0x00000600 +#define HW_CFG_MEF_ 0x00000020 +#define HW_CFG_BCE_ 0x00000002 +#define HW_CFG_LRST_ 0x00000008 + +#define PM_CTRL 0x20 +#define PM_CTL_PHY_RST_ 0x00000010 + +#define AFC_CFG 0x2C + +/* + * Hi watermark = 15.5Kb (~10 mtu pkts) + * low watermark = 3k (~2 mtu pkts) + * backpressure duration = ~ 350us + * Apply FC on any frame. + */ +#define AFC_CFG_DEFAULT 0x00F830A1 + +#define E2P_CMD 0x30 +#define E2P_CMD_BUSY_ 0x80000000 +#define E2P_CMD_READ_ 0x00000000 +#define E2P_CMD_TIMEOUT_ 0x00000400 +#define E2P_CMD_LOADED_ 0x00000200 +#define E2P_CMD_ADDR_ 0x000001FF + +#define E2P_DATA 0x34 + +#define BURST_CAP 0x38 + +#define INT_EP_CTL 0x68 +#define INT_EP_CTL_PHY_INT_ 0x00008000 + +#define BULK_IN_DLY 0x6C + +/* MAC CSRs */ +#define MAC_CR 0x100 +#define MAC_CR_MCPAS_ 0x00080000 +#define MAC_CR_PRMS_ 0x00040000 +#define MAC_CR_HPFILT_ 0x00002000 +#define MAC_CR_TXEN_ 0x00000008 +#define MAC_CR_RXEN_ 0x00000004 + +#define ADDRH 0x104 + +#define ADDRL 0x108 + +#define MII_ADDR 0x114 +#define MII_WRITE_ 0x02 +#define MII_BUSY_ 0x01 +#define MII_READ_ 0x00 /* ~of MII Write bit */ + +#define MII_DATA 0x118 + +#define FLOW 0x11C + +#define VLAN1 0x120 + +#define COE_CR 0x130 +#define Tx_COE_EN_ 0x00010000 +#define Rx_COE_EN_ 0x00000001 + +/* Vendor-specific PHY Definitions */ +#define PHY_INT_SRC 29 + +#define PHY_INT_MASK 30 +#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) +#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) +#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ + PHY_INT_MASK_LINK_DOWN_) + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 + +/* Some extra defines */ +#define HS_USB_PKT_SIZE 512 +#define FS_USB_PKT_SIZE 64 +/* 5/33 is lower limit for BURST_CAP to work */ +#define DEFAULT_HS_BURST_CAP_SIZE (5 * HS_USB_PKT_SIZE) +#define DEFAULT_FS_BURST_CAP_SIZE (33 * FS_USB_PKT_SIZE) +#define DEFAULT_BULK_IN_DELAY 0x00002000 +#define MAX_SINGLE_PACKET_SIZE 2048 +#define EEPROM_MAC_OFFSET 0x01 +#define SMSC95XX_INTERNAL_PHY_ID 1 +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ + +/* local defines */ +#define SMSC95XX_BASE_NAME "sms" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define RX_URB_SIZE DEFAULT_HS_BURST_CAP_SIZE +#define PHY_CONNECT_TIMEOUT 5000 + +#define TURBO_MODE + +#ifndef CONFIG_DM_ETH +/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */ +#endif + +/* driver private */ +struct smsc95xx_private { +#ifdef CONFIG_DM_ETH + struct ueth_data ueth; +#endif + size_t rx_urb_size; /* maximum USB URB size */ + u32 mac_cr; /* MAC control register value */ + int have_hwaddr; /* 1 if we have a hardware MAC address */ +}; + +/* + * Smsc95xx infrastructure commands + */ +static int smsc95xx_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); + if (len != sizeof(data)) { + debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d", + index, data, len); + return -EIO; + } + return 0; +} + +static int smsc95xx_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); + *data = tmpbuf[0]; + if (len != sizeof(*data)) { + debug("smsc95xx_read_reg failed: index=%d, len=%d", + index, len); + return -EIO; + } + + le32_to_cpus(data); + return 0; +} + +/* Loop until the read is completed with timeout */ +static int smsc95xx_phy_wait_not_busy(struct usb_device *udev) +{ + unsigned long start_time = get_timer(0); + u32 val; + + do { + smsc95xx_read_reg(udev, MII_ADDR, &val); + if (!(val & MII_BUSY_)) + return 0; + } while (get_timer(start_time) < 1000); + + return -ETIMEDOUT; +} + +static int smsc95xx_mdio_read(struct usb_device *udev, int phy_id, int idx) +{ + u32 val, addr; + + /* confirm MII not busy */ + if (smsc95xx_phy_wait_not_busy(udev)) { + debug("MII is busy in smsc95xx_mdio_read\n"); + return -ETIMEDOUT; + } + + /* set the address, index & direction (read from PHY) */ + addr = (phy_id << 11) | (idx << 6) | MII_READ_; + smsc95xx_write_reg(udev, MII_ADDR, addr); + + if (smsc95xx_phy_wait_not_busy(udev)) { + debug("Timed out reading MII reg %02X\n", idx); + return -ETIMEDOUT; + } + + smsc95xx_read_reg(udev, MII_DATA, &val); + + return (u16)(val & 0xFFFF); +} + +static void smsc95xx_mdio_write(struct usb_device *udev, int phy_id, int idx, + int regval) +{ + u32 val, addr; + + /* confirm MII not busy */ + if (smsc95xx_phy_wait_not_busy(udev)) { + debug("MII is busy in smsc95xx_mdio_write\n"); + return; + } + + val = regval; + smsc95xx_write_reg(udev, MII_DATA, val); + + /* set the address, index & direction (write to PHY) */ + addr = (phy_id << 11) | (idx << 6) | MII_WRITE_; + smsc95xx_write_reg(udev, MII_ADDR, addr); + + if (smsc95xx_phy_wait_not_busy(udev)) + debug("Timed out writing MII reg %02X\n", idx); +} + +static int smsc95xx_eeprom_confirm_not_busy(struct usb_device *udev) +{ + unsigned long start_time = get_timer(0); + u32 val; + + do { + smsc95xx_read_reg(udev, E2P_CMD, &val); + if (!(val & E2P_CMD_BUSY_)) + return 0; + udelay(40); + } while (get_timer(start_time) < 1 * 1000 * 1000); + + debug("EEPROM is busy\n"); + return -ETIMEDOUT; +} + +static int smsc95xx_wait_eeprom(struct usb_device *udev) +{ + unsigned long start_time = get_timer(0); + u32 val; + + do { + smsc95xx_read_reg(udev, E2P_CMD, &val); + if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) + break; + udelay(40); + } while (get_timer(start_time) < 1 * 1000 * 1000); + + if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) { + debug("EEPROM read operation timeout\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int smsc95xx_read_eeprom(struct usb_device *udev, u32 offset, u32 length, + u8 *data) +{ + u32 val; + int i, ret; + + ret = smsc95xx_eeprom_confirm_not_busy(udev); + if (ret) + return ret; + + for (i = 0; i < length; i++) { + val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); + smsc95xx_write_reg(udev, E2P_CMD, val); + + ret = smsc95xx_wait_eeprom(udev); + if (ret < 0) + return ret; + + smsc95xx_read_reg(udev, E2P_DATA, &val); + data[i] = val & 0xFF; + offset++; + } + return 0; +} + +/* + * 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 = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + bmcr |= BMCR_ANRESTART; + smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr); + r = 0; + } + return r; +} + +static int smsc95xx_phy_initialize(struct usb_device *udev, + struct ueth_data *dev) +{ + smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET); + smsc95xx_mdio_write(udev, dev->phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA | + ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + + /* read to clear */ + smsc95xx_mdio_read(udev, dev->phy_id, PHY_INT_SRC); + + smsc95xx_mdio_write(udev, dev->phy_id, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT_); + mii_nway_restart(udev, dev); + + debug("phy initialised succesfully\n"); + return 0; +} + +static int smsc95xx_init_mac_address(unsigned char *enetaddr, + struct usb_device *udev) +{ + int ret; + + /* try reading mac address from EEPROM */ + ret = smsc95xx_read_eeprom(udev, EEPROM_MAC_OFFSET, ETH_ALEN, enetaddr); + if (ret) + return ret; + + if (is_valid_ethaddr(enetaddr)) { + /* eeprom values are valid so use them */ + debug("MAC address read from EEPROM\n"); + return 0; + } + + /* + * No eeprom, or eeprom values are invalid. Generating a random MAC + * address is not safe. Just return an error. + */ + debug("Invalid MAC address read from EEPROM\n"); + + return -ENXIO; +} + +static int smsc95xx_write_hwaddr_common(struct usb_device *udev, + struct smsc95xx_private *priv, + unsigned char *enetaddr) +{ + u32 addr_lo = get_unaligned_le32(&enetaddr[0]); + u32 addr_hi = get_unaligned_le16(&enetaddr[4]); + int ret; + + /* set hardware address */ + debug("** %s()\n", __func__); + ret = smsc95xx_write_reg(udev, ADDRL, addr_lo); + if (ret < 0) + return ret; + + ret = smsc95xx_write_reg(udev, ADDRH, addr_hi); + if (ret < 0) + return ret; + + debug("MAC %pM\n", enetaddr); + priv->have_hwaddr = 1; + + return 0; +} + +/* Enable or disable Tx & Rx checksum offload engines */ +static int smsc95xx_set_csums(struct usb_device *udev, int use_tx_csum, + int use_rx_csum) +{ + u32 read_buf; + int ret = smsc95xx_read_reg(udev, COE_CR, &read_buf); + if (ret < 0) + return ret; + + if (use_tx_csum) + read_buf |= Tx_COE_EN_; + else + read_buf &= ~Tx_COE_EN_; + + if (use_rx_csum) + read_buf |= Rx_COE_EN_; + else + read_buf &= ~Rx_COE_EN_; + + ret = smsc95xx_write_reg(udev, COE_CR, read_buf); + if (ret < 0) + return ret; + + debug("COE_CR = 0x%08x\n", read_buf); + return 0; +} + +static void smsc95xx_set_multicast(struct smsc95xx_private *priv) +{ + /* No multicast in u-boot */ + priv->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); +} + +/* starts the TX path */ +static void smsc95xx_start_tx_path(struct usb_device *udev, + struct smsc95xx_private *priv) +{ + u32 reg_val; + + /* Enable Tx at MAC */ + priv->mac_cr |= MAC_CR_TXEN_; + + smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr); + + /* Enable Tx at SCSRs */ + reg_val = TX_CFG_ON_; + smsc95xx_write_reg(udev, TX_CFG, reg_val); +} + +/* Starts the Receive path */ +static void smsc95xx_start_rx_path(struct usb_device *udev, + struct smsc95xx_private *priv) +{ + priv->mac_cr |= MAC_CR_RXEN_; + smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr); +} + +static int smsc95xx_init_common(struct usb_device *udev, struct ueth_data *dev, + struct smsc95xx_private *priv, + unsigned char *enetaddr) +{ + int ret; + u32 write_buf; + u32 read_buf; + u32 burst_cap; + int timeout; +#define TIMEOUT_RESOLUTION 50 /* ms */ + int link_detected; + + debug("** %s()\n", __func__); + dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */ + + write_buf = HW_CFG_LRST_; + ret = smsc95xx_write_reg(udev, HW_CFG, write_buf); + if (ret < 0) + return ret; + + timeout = 0; + do { + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + udelay(10 * 1000); + timeout++; + } while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); + + if (timeout >= 100) { + debug("timeout waiting for completion of Lite Reset\n"); + return -ETIMEDOUT; + } + + write_buf = PM_CTL_PHY_RST_; + ret = smsc95xx_write_reg(udev, PM_CTRL, write_buf); + if (ret < 0) + return ret; + + timeout = 0; + do { + ret = smsc95xx_read_reg(udev, PM_CTRL, &read_buf); + if (ret < 0) + return ret; + udelay(10 * 1000); + timeout++; + } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); + if (timeout >= 100) { + debug("timeout waiting for PHY Reset\n"); + return -ETIMEDOUT; + } +#ifndef CONFIG_DM_ETH + if (!priv->have_hwaddr && smsc95xx_init_mac_address(enetaddr, udev) == + 0) + priv->have_hwaddr = 1; +#endif + if (!priv->have_hwaddr) { + puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n"); + return -EADDRNOTAVAIL; + } + ret = smsc95xx_write_hwaddr_common(udev, priv, enetaddr); + if (ret < 0) + return ret; + +#ifdef TURBO_MODE + if (dev->pusb_dev->speed == USB_SPEED_HIGH) { + burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; + priv->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; + } else { + burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; + priv->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; + } +#else + burst_cap = 0; + priv->rx_urb_size = MAX_SINGLE_PACKET_SIZE; +#endif + debug("rx_urb_size=%ld\n", (ulong)priv->rx_urb_size); + + ret = smsc95xx_write_reg(udev, BURST_CAP, burst_cap); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(udev, BURST_CAP, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf); + + read_buf = DEFAULT_BULK_IN_DELAY; + ret = smsc95xx_write_reg(udev, BULK_IN_DLY, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(udev, BULK_IN_DLY, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from BULK_IN_DLY after writing: " + "0x%08x\n", read_buf); + + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from HW_CFG: 0x%08x\n", read_buf); + +#ifdef TURBO_MODE + read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_); +#endif + read_buf &= ~HW_CFG_RXDOFF_; + +#define NET_IP_ALIGN 0 + read_buf |= NET_IP_ALIGN << 9; + + ret = smsc95xx_write_reg(udev, HW_CFG, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf); + if (ret < 0) + return ret; + debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf); + + write_buf = 0xFFFFFFFF; + ret = smsc95xx_write_reg(udev, INT_STS, write_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(udev, ID_REV, &read_buf); + if (ret < 0) + return ret; + debug("ID_REV = 0x%08x\n", read_buf); + + /* Configure GPIO pins as LED outputs */ + write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | + LED_GPIO_CFG_FDX_LED; + ret = smsc95xx_write_reg(udev, LED_GPIO_CFG, write_buf); + if (ret < 0) + return ret; + debug("LED_GPIO_CFG set\n"); + + /* Init Tx */ + write_buf = 0; + ret = smsc95xx_write_reg(udev, FLOW, write_buf); + if (ret < 0) + return ret; + + read_buf = AFC_CFG_DEFAULT; + ret = smsc95xx_write_reg(udev, AFC_CFG, read_buf); + if (ret < 0) + return ret; + + ret = smsc95xx_read_reg(udev, MAC_CR, &priv->mac_cr); + if (ret < 0) + return ret; + + /* Init Rx. Set Vlan */ + write_buf = (u32)ETH_P_8021Q; + ret = smsc95xx_write_reg(udev, VLAN1, write_buf); + if (ret < 0) + return ret; + + /* Disable checksum offload engines */ + ret = smsc95xx_set_csums(udev, 0, 0); + if (ret < 0) { + debug("Failed to set csum offload: %d\n", ret); + return ret; + } + smsc95xx_set_multicast(priv); + + ret = smsc95xx_phy_initialize(udev, dev); + if (ret < 0) + return ret; + ret = smsc95xx_read_reg(udev, INT_EP_CTL, &read_buf); + if (ret < 0) + return ret; + + /* enable PHY interrupts */ + read_buf |= INT_EP_CTL_PHY_INT_; + + ret = smsc95xx_write_reg(udev, INT_EP_CTL, read_buf); + if (ret < 0) + return ret; + + smsc95xx_start_tx_path(udev, priv); + smsc95xx_start_rx_path(udev, priv); + + timeout = 0; + do { + link_detected = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMSR) + & BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + udelay(TIMEOUT_RESOLUTION * 1000); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + if (link_detected) { + if (timeout != 0) + printf("done.\n"); + } else { + printf("unable to connect.\n"); + return -EIO; + } + return 0; +} + +static int smsc95xx_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; + + tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_; + tx_cmd_b = (u32)length; + 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); + 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; +} + +#ifndef CONFIG_DM_ETH +/* + * Smsc95xx callbacks + */ +static int smsc95xx_init(struct eth_device *eth, struct bd_info *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct usb_device *udev = dev->pusb_dev; + struct smsc95xx_private *priv = + (struct smsc95xx_private *)dev->dev_priv; + + return smsc95xx_init_common(udev, dev, priv, eth->enetaddr); +} + +static int smsc95xx_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return smsc95xx_send_common(dev, packet, length); +} + +static int smsc95xx_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, RX_URB_SIZE); + unsigned char *buf_ptr; + int err; + int actual_len; + u32 packet_len; + int cur_buf_align; + + debug("** %s()\n", __func__); + 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); + debug("Rx: len = %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; + while (actual_len > 0) { + /* + * 1st 4 bytes contain the length of the actual data plus error + * info. Extract data length. + */ + if (actual_len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + return -EIO; + } + memcpy(&packet_len, buf_ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (packet_len & RX_STS_ES_) { + debug("Rx: Error header=%#x", packet_len); + return -EIO; + } + packet_len = ((packet_len & RX_STS_FL_) >> 16); + + if (packet_len > actual_len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + return -EIO; + } + + /* Notify net stack */ + net_process_received_packet(buf_ptr + sizeof(packet_len), + packet_len - 4); + + /* Adjust for next iteration */ + actual_len -= sizeof(packet_len) + packet_len; + buf_ptr += sizeof(packet_len) + packet_len; + cur_buf_align = (ulong)buf_ptr - (ulong)recv_buf; + + if (cur_buf_align & 0x03) { + int align = 4 - (cur_buf_align & 0x03); + + actual_len -= align; + buf_ptr += align; + } + } + return err; +} + +static void smsc95xx_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +static int smsc95xx_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = eth->priv; + struct usb_device *udev = dev->pusb_dev; + struct smsc95xx_private *priv = dev->dev_priv; + + return smsc95xx_write_hwaddr_common(udev, priv, eth->enetaddr); +} + +/* + * SMSC probing functions + */ +void smsc95xx_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct smsc95xx_dongle { + unsigned short vendor; + unsigned short product; +}; + +static const struct smsc95xx_dongle smsc95xx_dongles[] = { + { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */ + { 0x0424, 0x9500 }, /* LAN9500 Ethernet */ + { 0x0424, 0x9730 }, /* LAN9730 Ethernet (HSIC) */ + { 0x0424, 0x9900 }, /* SMSC9500 USB Ethernet Device (SAL10) */ + { 0x0424, 0x9e00 }, /* LAN9500A Ethernet */ + { 0x0000, 0x0000 } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an SMSC device */ +int smsc95xx_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; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == smsc95xx_dongles[i].vendor && + dev->descriptor.idProduct == smsc95xx_dongles[i].product) + /* Found a supported dongle */ + break; + } + if (smsc95xx_dongles[i].vendor == 0) + return 0; + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected\n"); + 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; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct smsc95xx_private)); + if (!ss->dev_priv) + return 0; + + return 1; +} + +int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + debug("** %s()\n", __func__); + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", SMSC95XX_BASE_NAME, curr_eth_dev++); + eth->init = smsc95xx_init; + eth->send = smsc95xx_send; + eth->recv = smsc95xx_recv; + eth->halt = smsc95xx_halt; + eth->write_hwaddr = smsc95xx_write_hwaddr; + eth->priv = ss; + return 1; +} +#endif /* !CONFIG_DM_ETH */ + +#ifdef CONFIG_DM_ETH +static int smsc95xx_eth_start(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct smsc95xx_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + + /* Driver-model Ethernet ensures we have this */ + priv->have_hwaddr = 1; + + return smsc95xx_init_common(udev, &priv->ueth, priv, pdata->enetaddr); +} + +void smsc95xx_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int smsc95xx_eth_send(struct udevice *dev, void *packet, int length) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + + return smsc95xx_send_common(&priv->ueth, packet, length); +} + +int smsc95xx_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + uint8_t *ptr; + int ret, len; + u32 packet_len; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, RX_URB_SIZE); + if (ret == -EAGAIN) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + /* + * 1st 4 bytes contain the length of the actual data plus error info. + * Extract data length. + */ + if (len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + goto err; + } + memcpy(&packet_len, ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (packet_len & RX_STS_ES_) { + debug("Rx: Error header=%#x", packet_len); + goto err; + } + packet_len = ((packet_len & RX_STS_FL_) >> 16); + + if (packet_len > len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + *packetp = ptr + sizeof(packet_len); + return packet_len - 4; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -EINVAL; +} + +static int smsc95xx_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + + packet_len = ALIGN(packet_len + sizeof(u32), 4); + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int smsc95xx_write_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + struct smsc95xx_private *priv = dev_get_priv(dev); + + return smsc95xx_write_hwaddr_common(udev, priv, pdata->enetaddr); +} + +int smsc95xx_read_rom_hwaddr(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct eth_pdata *pdata = dev_get_plat(dev); + int ret; + + ret = smsc95xx_init_mac_address(pdata->enetaddr, udev); + if (ret) + memset(pdata->enetaddr, 0, 6); + + return 0; +} + +static int smsc95xx_eth_probe(struct udevice *dev) +{ + struct smsc95xx_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + + return usb_ether_register(dev, ueth, RX_URB_SIZE); +} + +static const struct eth_ops smsc95xx_eth_ops = { + .start = smsc95xx_eth_start, + .send = smsc95xx_eth_send, + .recv = smsc95xx_eth_recv, + .free_pkt = smsc95xx_free_pkt, + .stop = smsc95xx_eth_stop, + .write_hwaddr = smsc95xx_write_hwaddr, + .read_rom_hwaddr = smsc95xx_read_rom_hwaddr, +}; + +U_BOOT_DRIVER(smsc95xx_eth) = { + .name = "smsc95xx_eth", + .id = UCLASS_ETH, + .probe = smsc95xx_eth_probe, + .ops = &smsc95xx_eth_ops, + .priv_auto = sizeof(struct smsc95xx_private), + .plat_auto = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id smsc95xx_eth_id_table[] = { + { USB_DEVICE(0x05ac, 0x1402) }, + { USB_DEVICE(0x0424, 0xec00) }, /* LAN9512/LAN9514 Ethernet */ + { USB_DEVICE(0x0424, 0x9500) }, /* LAN9500 Ethernet */ + { USB_DEVICE(0x0424, 0x9730) }, /* LAN9730 Ethernet (HSIC) */ + { USB_DEVICE(0x0424, 0x9900) }, /* SMSC9500 USB Ethernet (SAL10) */ + { USB_DEVICE(0x0424, 0x9e00) }, /* LAN9500A Ethernet */ + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(smsc95xx_eth, smsc95xx_eth_id_table); +#endif diff --git a/roms/u-boot/drivers/usb/eth/usb_ether.c b/roms/u-boot/drivers/usb/eth/usb_ether.c new file mode 100644 index 000000000..e368ecda0 --- /dev/null +++ b/roms/u-boot/drivers/usb/eth/usb_ether.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <net.h> +#include <usb.h> +#include <asm/cache.h> +#include <dm/device-internal.h> + +#include "usb_ether.h" + +#ifdef CONFIG_DM_ETH + +#define USB_BULK_RECV_TIMEOUT 500 + +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct usb_interface_descriptor *iface_desc; + bool ep_in_found = false, ep_out_found = false; + struct usb_interface *iface; + const int ifnum = 0; /* Always use interface 0 */ + int ret, i; + + iface = &udev->config.if_desc[ifnum]; + iface_desc = &udev->config.if_desc[ifnum].desc; + + /* Initialize the ueth_data structure with some useful info */ + ueth->ifnum = ifnum; + ueth->subclass = iface_desc->bInterfaceSubClass; + ueth->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++) { + int ep_addr = iface->ep_desc[i].bEndpointAddress; + + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (ep_addr & USB_DIR_IN && !ep_in_found) { + ueth->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = true; + } else if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ueth->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = true; + } + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ueth->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ueth->irqinterval = iface->ep_desc[i].bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", ueth->ep_in, ueth->ep_out, + ueth->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ueth->ep_in || !ueth->ep_out || !ueth->ep_int) { + debug("%s: %s: Cannot find endpoints\n", __func__, dev->name); + return -ENXIO; + } + + ueth->rxsize = rxsize; + ueth->rxbuf = memalign(ARCH_DMA_MINALIGN, rxsize); + if (!ueth->rxbuf) + return -ENOMEM; + + ret = usb_set_interface(udev, iface_desc->bInterfaceNumber, ifnum); + if (ret) { + debug("%s: %s: Cannot set interface: %d\n", __func__, dev->name, + ret); + return ret; + } + ueth->pusb_dev = udev; + + return 0; +} + +int usb_ether_deregister(struct ueth_data *ueth) +{ + return 0; +} + +int usb_ether_receive(struct ueth_data *ueth, int rxsize) +{ + int actual_len; + int ret; + + if (rxsize > ueth->rxsize) + return -EINVAL; + ret = usb_bulk_msg(ueth->pusb_dev, + usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), + ueth->rxbuf, rxsize, &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", rxsize, actual_len, ret); + if (ret) { + printf("Rx: failed to receive: %d\n", ret); + return ret; + } + if (actual_len > rxsize) { + debug("Rx: received too many bytes %d\n", actual_len); + return -ENOSPC; + } + ueth->rxlen = actual_len; + ueth->rxptr = 0; + + return actual_len ? 0 : -EAGAIN; +} + +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes) +{ + ueth->rxptr += num_bytes; + if (num_bytes < 0 || ueth->rxptr >= ueth->rxlen) + ueth->rxlen = 0; +} + +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp) +{ + if (!ueth->rxlen) + return 0; + + *ptrp = &ueth->rxbuf[ueth->rxptr]; + + return ueth->rxlen - ueth->rxptr; +} + +#else + +typedef void (*usb_eth_before_probe)(void); +typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss); +typedef int (*usb_eth_get_info)(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *dev_desc); + +struct usb_eth_prob_dev { + usb_eth_before_probe before_probe; /* optional */ + usb_eth_probe probe; + usb_eth_get_info get_info; +}; + +/* driver functions go here, each bracketed by #ifdef CONFIG_USB_ETHER_xxx */ +static const struct usb_eth_prob_dev prob_dev[] = { +#ifdef CONFIG_USB_ETHER_ASIX + { + .before_probe = asix_eth_before_probe, + .probe = asix_eth_probe, + .get_info = asix_eth_get_info, + }, +#endif +#ifdef CONFIG_USB_ETHER_ASIX88179 + { + .before_probe = ax88179_eth_before_probe, + .probe = ax88179_eth_probe, + .get_info = ax88179_eth_get_info, + }, +#endif +#ifdef CONFIG_USB_ETHER_MCS7830 + { + .before_probe = mcs7830_eth_before_probe, + .probe = mcs7830_eth_probe, + .get_info = mcs7830_eth_get_info, + }, +#endif +#ifdef CONFIG_USB_ETHER_SMSC95XX + { + .before_probe = smsc95xx_eth_before_probe, + .probe = smsc95xx_eth_probe, + .get_info = smsc95xx_eth_get_info, + }, +#endif +#ifdef CONFIG_USB_ETHER_RTL8152 + { + .before_probe = r8152_eth_before_probe, + .probe = r8152_eth_probe, + .get_info = r8152_eth_get_info, + }, +#endif + { }, /* END */ +}; + +static int usb_max_eth_dev; /* number of highest available usb eth device */ +static struct ueth_data usb_eth[USB_MAX_ETH_DEV]; + +/******************************************************************************* + * tell if current ethernet device is a usb dongle + */ +int is_eth_dev_on_usb_host(void) +{ + int i; + struct eth_device *dev = eth_get_dev(); + + if (dev) { + for (i = 0; i < usb_max_eth_dev; i++) + if (&usb_eth[i].eth_dev == dev) + return 1; + } + return 0; +} + +/* + * Given a USB device, ask each driver if it can support it, and attach it + * to the first driver that says 'yes' + */ +static void probe_valid_drivers(struct usb_device *dev) +{ + struct eth_device *eth; + int j; + + for (j = 0; prob_dev[j].probe && prob_dev[j].get_info; j++) { + if (!prob_dev[j].probe(dev, 0, &usb_eth[usb_max_eth_dev])) + continue; + /* + * ok, it is a supported eth device. Get info and fill it in + */ + eth = &usb_eth[usb_max_eth_dev].eth_dev; + if (prob_dev[j].get_info(dev, + &usb_eth[usb_max_eth_dev], + eth)) { + /* found proper driver */ + /* register with networking stack */ + usb_max_eth_dev++; + + /* + * usb_max_eth_dev must be incremented prior to this + * call since eth_current_changed (internally called) + * relies on it + */ + eth_register(eth); + if (eth_write_hwaddr(eth, "usbeth", + usb_max_eth_dev - 1)) + puts("Warning: failed to set MAC address\n"); + break; + } + } + } + +/******************************************************************************* + * scan the usb and reports device info + * to the user if mode = 1 + * returns current device or -1 if no + */ +int usb_host_eth_scan(int mode) +{ + int i, old_async; + + if (mode == 1) + printf(" scanning usb for ethernet devices... "); + + old_async = usb_disable_asynch(1); /* asynch transfer not allowed */ + + /* unregister a previously detected device */ + for (i = 0; i < usb_max_eth_dev; i++) + eth_unregister(&usb_eth[i].eth_dev); + + memset(usb_eth, 0, sizeof(usb_eth)); + + for (i = 0; prob_dev[i].probe; i++) { + if (prob_dev[i].before_probe) + prob_dev[i].before_probe(); + } + + usb_max_eth_dev = 0; +#if CONFIG_IS_ENABLED(DM_USB) + /* + * TODO: We should add U_BOOT_USB_DEVICE() declarations to each USB + * Ethernet driver and then most of this file can be removed. + */ + struct udevice *bus; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_USB, &uc); + if (ret) + return ret; + uclass_foreach_dev(bus, uc) { + for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + + dev = usb_get_dev_index(bus, i); /* get device */ + debug("i=%d, %s\n", i, dev ? dev->dev->name : "(done)"); + if (!dev) + break; /* no more devices available */ + + /* + * find valid usb_ether driver for this device, + * if any + */ + probe_valid_drivers(dev); + + /* check limit */ + if (usb_max_eth_dev == USB_MAX_ETH_DEV) + break; + } /* for */ + } +#else + for (i = 0; i < USB_MAX_DEVICE; i++) { + struct usb_device *dev; + + dev = usb_get_dev_index(i); /* get device */ + debug("i=%d\n", i); + if (!dev) + break; /* no more devices available */ + + /* find valid usb_ether driver for this device, if any */ + probe_valid_drivers(dev); + + /* check limit */ + if (usb_max_eth_dev == USB_MAX_ETH_DEV) + break; + } /* for */ +#endif + if (usb_max_eth_dev == USB_MAX_ETH_DEV) { + printf("max USB Ethernet Device reached: %d stopping\n", + usb_max_eth_dev); + } + usb_disable_asynch(old_async); /* restore asynch value */ + printf("%d Ethernet Device(s) found\n", usb_max_eth_dev); + if (usb_max_eth_dev > 0) + return 0; + return -1; +} +#endif |