Status
Not open for further replies.

GBAtemp 20th Anniversary giveaway - WEEK FOUR - Win a custom-built Game Boy from Jayboymodz - 24hrs remain!

week-4 (1).png

To celebrate GBAtemp's 20th birthday which fell on the 22nd October we have partnered with some amazing sponsors to bring you GBAtemp's biggest ever giveaway with almost $2,000 worth of prizes to win!

This is the fourth and final weekly giveaway before the final raffle that we're going to draw on October 31st where you have the chance to win the grand prize of a Nintendo Switch OLED and game of your choice!

The giveaways are open to all registered members worldwide! (Don't have an account? Sign up here for free)

Week Four - NOW ACTIVE!​

October 24th - 30th​



Sponsored by
Jayboy Logo.png

"Modding Gameboys since 2018, and specializing in custom retro consoles and handhelds! With personal passion and support from the retro gaming community, Jayboymodz continues to give your favorite consoles from the past the revamp they need!"

For the final giveaway we are working with the talented JayBoyModz - a professional modder who specialises in custom Game Boy's and controllers of all types. JayBoyModz have been providing custom built products of the highest quality for years - our Chief Editor @Chary reviewed a custom, purpose-built GBA back in 2019 - check out her full review here.

The lucky winner of the fourth giveaway will have the incredible opportunity to receive their own custom-built, fully-specced and blinged-out Game Boy (GB,GBC,GBA etc.) made to their liking up to a value of $300 USD! An incredible opportunity and a rather apt prize as we celebrate the 20th anniversary of GBAtemp!

IMG_2769_ed37be74-a63a-47ab-941a.jpg

If that's not enough, runners-up will have the chance to win Amazon gift cards with the usual $50, $25 and $10 values and a BONUS $100 (USD/equivalent) card available in week four!

:arrow: JayBoyModz

How to Enter Entry is easy and free!

To enter this weeks giveaway - all you need to do is post a reply below! It's as simple as that!

All registered members are welcome to enter for free! One entry per giveaway - duplicate accounts will be detected and all related accounts will be disqualified.

Post in this thread (and the previous weeks giveaways) to get your entry/entries into the final grand prize raffle on October 31st - GBAtemp patrons get a bonus entry for a maximum of 5!

Results for all giveaways will be drawn on October 31st!

Good luck!

This weeks giveaway will close at 10am October 31st 2022.


GBAtemp's 20th Anniversary giveaway details


tall-4.png

Grand Prize Raffle
October 31st​


switch-th.png

Our celebration month will conclude on October 31st with a grand prize raffle with the opportunity to win a Nintendo Switch OLED and game bundle!

Fancy a shot at winning the grand prize? Entry into the raffle is simple! To enter all you have to do is to have participated in any prior weeks giveaway!

But that's not all! You can gain EXTRA entries into the grand prize raffle for an increased chance of winning by posting in each prior weeks giveaways or by becoming a GBAtemp patron.

The more giveways you enter - the higher your chance of winning the grand prize raffle!

Week Four sponsored by Jayboymodz is NOW OPEN!

We're incredibly excited and proud to be celebrating the month of October with you as we approach GBAtemp.net's 20th birthday! We'll have more to share with you regarding our birthday on October 22nd but in the mean time we sincerely hope you are enjoying our giveaways and raffle and wish you the best of luck!

A massive thank you to our sponsors - 8BitDo, DroiX (UK), DroiX (EU & USA), Analogue and JayBoyModz - please show them some love via the links below.

:arrow: 8BitDo
:arrow: DroiX (UK customers)
:arrow: DroiX (EU and USA Customers)
:arrow: Analogue Pocket
:arrow: JayBoyModz

- @Costello & @shaunj66 and all the GBAtemp Staff

Terms and conditions
1. The giveaway and raffle are open to all registered GBAtemp members. 2. The staffs decision is final. No cash alternative. 3. Contest is open to all world-wide but in some instances it may not be possible to organise delivery to some countries - in this case an alternative prize may be offered. 4. Duplicate accounts are forbidden - anyone found to be entering via duplicate accounts will be disqualified. 5. These terms and conditions can be amended at any time. 6. By entering a giveaway or the raffle you agree to be bound to these terms and that the staff decision is final. 7. Personal information provided for shipping will be kept in the strictest confidence and not sold to third parties but may be shared with the competition sponsors for delivery.
 

Attachments

  • P1077373.JPG
    P1077373.JPG
    65.5 KB · Views: 83

porygonz2001

Member
Newcomer
Joined
Nov 15, 2018
Messages
23
Trophies
0
Age
22
XP
132
Country
Colombia
C:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * ASIX AX8817X based USB 2.0 Ethernet Devices
 * Copyright (C) 2003-2006 David Hollis <[email protected]>
 * Copyright (C) 2005 Phil Chang <[email protected]>
 * Copyright (C) 2006 James Painter <[email protected]>
 * Copyright (c) 2002-2003 TiVo Inc.
 */

#include "asix.h"

#define PHY_MODE_MARVELL    0x0000
#define MII_MARVELL_LED_CTRL    0x0018
#define MII_MARVELL_STATUS    0x001b
#define MII_MARVELL_CTRL    0x0014

#define MARVELL_LED_MANUAL    0x0019

#define MARVELL_STATUS_HWCFG    0x0004

#define MARVELL_CTRL_TXDELAY    0x0002
#define MARVELL_CTRL_RXDELAY    0x0080

#define    PHY_MODE_RTL8211CL    0x000C

#define AX88772A_PHY14H        0x14
#define AX88772A_PHY14H_DEFAULT 0x442C

#define AX88772A_PHY15H        0x15
#define AX88772A_PHY15H_DEFAULT 0x03C8

#define AX88772A_PHY16H        0x16
#define AX88772A_PHY16H_DEFAULT 0x4044

struct ax88172_int_data {
    __le16 res1;
    u8 link;
    __le16 res2;
    u8 status;
    __le16 res3;
} __packed;

static void asix_status(struct usbnet *dev, struct urb *urb)
{
    struct ax88172_int_data *event;
    int link;

    if (urb->actual_length < 8)
        return;

    event = urb->transfer_buffer;
    link = event->link & 0x01;
    if (netif_carrier_ok(dev->net) != link) {
        usbnet_link_change(dev, link, 1);
        netdev_dbg(dev->net, "Link Status is: %d\n", link);
    }
}

static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr)
{
    if (is_valid_ether_addr(addr)) {
        eth_hw_addr_set(dev->net, addr);
    } else {
        netdev_info(dev->net, "invalid hw address, using random\n");
        eth_hw_addr_random(dev->net);
    }
}

/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
static u32 asix_get_phyid(struct usbnet *dev)
{
    int phy_reg;
    u32 phy_id;
    int i;

    /* Poll for the rare case the FW or phy isn't ready yet.  */
    for (i = 0; i < 100; i++) {
        phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
        if (phy_reg < 0)
            return 0;
        if (phy_reg != 0 && phy_reg != 0xFFFF)
            break;
        mdelay(1);
    }

    if (phy_reg <= 0 || phy_reg == 0xFFFF)
        return 0;

    phy_id = (phy_reg & 0xffff) << 16;

    phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2);
    if (phy_reg < 0)
        return 0;

    phy_id |= (phy_reg & 0xffff);

    return phy_id;
}

static u32 asix_get_link(struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);

    return mii_link_ok(&dev->mii);
}

static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
{
    struct usbnet *dev = netdev_priv(net);

    return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
}

/* We need to override some ethtool_ops so we require our
   own structure so we don't interfere with other usbnet
   devices that may be connected at the same time. */
static const struct ethtool_ops ax88172_ethtool_ops = {
    .get_drvinfo        = asix_get_drvinfo,
    .get_link        = asix_get_link,
    .get_msglevel        = usbnet_get_msglevel,
    .set_msglevel        = usbnet_set_msglevel,
    .get_wol        = asix_get_wol,
    .set_wol        = asix_set_wol,
    .get_eeprom_len        = asix_get_eeprom_len,
    .get_eeprom        = asix_get_eeprom,
    .set_eeprom        = asix_set_eeprom,
    .nway_reset        = usbnet_nway_reset,
    .get_link_ksettings    = usbnet_get_link_ksettings_mii,
    .set_link_ksettings    = usbnet_set_link_ksettings_mii,
};

static void ax88172_set_multicast(struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);
    struct asix_data *data = (struct asix_data *)&dev->data;
    u8 rx_ctl = 0x8c;

    if (net->flags & IFF_PROMISC) {
        rx_ctl |= 0x01;
    } else if (net->flags & IFF_ALLMULTI ||
           netdev_mc_count(net) > AX_MAX_MCAST) {
        rx_ctl |= 0x02;
    } else if (netdev_mc_empty(net)) {
        /* just broadcast and directed */
    } else {
        /* We use the 20 byte dev->data
         * for our 8 byte filter buffer
         * to avoid allocating memory that
         * is tricky to free later */
        struct netdev_hw_addr *ha;
        u32 crc_bits;

        memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);

        /* Build the multicast hash filter. */
        netdev_for_each_mc_addr(ha, net) {
            crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
            data->multi_filter[crc_bits >> 3] |=
                1 << (crc_bits & 7);
        }

        asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
                   AX_MCAST_FILTER_SIZE, data->multi_filter);

        rx_ctl |= 0x10;
    }

    asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
}

static int ax88172_link_reset(struct usbnet *dev)
{
    u8 mode;
    struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };

    mii_check_media(&dev->mii, 1, 1);
    mii_ethtool_gset(&dev->mii, &ecmd);
    mode = AX88172_MEDIUM_DEFAULT;

    if (ecmd.duplex != DUPLEX_FULL)
        mode |= ~AX88172_MEDIUM_FD;

    netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
           ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);

    asix_write_medium_mode(dev, mode, 0);

    return 0;
}

static const struct net_device_ops ax88172_netdev_ops = {
    .ndo_open        = usbnet_open,
    .ndo_stop        = usbnet_stop,
    .ndo_start_xmit        = usbnet_start_xmit,
    .ndo_tx_timeout        = usbnet_tx_timeout,
    .ndo_change_mtu        = usbnet_change_mtu,
    .ndo_get_stats64    = dev_get_tstats64,
    .ndo_set_mac_address     = eth_mac_addr,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_eth_ioctl        = asix_ioctl,
    .ndo_set_rx_mode    = ax88172_set_multicast,
};

static void asix_phy_reset(struct usbnet *dev, unsigned int reset_bits)
{
    unsigned int timeout = 5000;

    asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, reset_bits);

    /* give phy_id a chance to process reset */
    udelay(500);

    /* See IEEE 802.3 "22.2.4.1.1 Reset": 500ms max */
    while (timeout--) {
        if (asix_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR)
                            & BMCR_RESET)
            udelay(100);
        else
            return;
    }

    netdev_err(dev->net, "BMCR_RESET timeout on phy_id %d\n",
           dev->mii.phy_id);
}

static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
{
    int ret = 0;
    u8 buf[ETH_ALEN] = {0};
    int i;
    unsigned long gpio_bits = dev->driver_info->data;

    usbnet_get_endpoints(dev,intf);

    /* Toggle the GPIOs in a manufacturer/model specific way */
    for (i = 2; i >= 0; i--) {
        ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
                (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0);
        if (ret < 0)
            goto out;
        msleep(5);
    }

    ret = asix_write_rx_ctl(dev, 0x80, 0);
    if (ret < 0)
        goto out;

    /* Get the MAC address */
    ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
                0, 0, ETH_ALEN, buf, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n",
               ret);
        goto out;
    }

    asix_set_netdev_dev_addr(dev, buf);

    /* Initialize MII structure */
    dev->mii.dev = dev->net;
    dev->mii.mdio_read = asix_mdio_read;
    dev->mii.mdio_write = asix_mdio_write;
    dev->mii.phy_id_mask = 0x3f;
    dev->mii.reg_num_mask = 0x1f;

    dev->mii.phy_id = asix_read_phy_addr(dev, true);
    if (dev->mii.phy_id < 0)
        return dev->mii.phy_id;

    dev->net->netdev_ops = &ax88172_netdev_ops;
    dev->net->ethtool_ops = &ax88172_ethtool_ops;
    dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
    dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */

    asix_phy_reset(dev, BMCR_RESET);
    asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
        ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
    mii_nway_restart(&dev->mii);

    return 0;

out:
    return ret;
}

static void ax88772_ethtool_get_strings(struct net_device *netdev, u32 sset,
                    u8 *data)
{
    switch (sset) {
    case ETH_SS_TEST:
        net_selftest_get_strings(data);
        break;
    }
}

static int ax88772_ethtool_get_sset_count(struct net_device *ndev, int sset)
{
    switch (sset) {
    case ETH_SS_TEST:
        return net_selftest_get_count();
    default:
        return -EOPNOTSUPP;
    }
}

static void ax88772_ethtool_get_pauseparam(struct net_device *ndev,
                      struct ethtool_pauseparam *pause)
{
    struct usbnet *dev = netdev_priv(ndev);
    struct asix_common_private *priv = dev->driver_priv;

    phylink_ethtool_get_pauseparam(priv->phylink, pause);
}

static int ax88772_ethtool_set_pauseparam(struct net_device *ndev,
                     struct ethtool_pauseparam *pause)
{
    struct usbnet *dev = netdev_priv(ndev);
    struct asix_common_private *priv = dev->driver_priv;

    return phylink_ethtool_set_pauseparam(priv->phylink, pause);
}

static const struct ethtool_ops ax88772_ethtool_ops = {
    .get_drvinfo        = asix_get_drvinfo,
    .get_link        = usbnet_get_link,
    .get_msglevel        = usbnet_get_msglevel,
    .set_msglevel        = usbnet_set_msglevel,
    .get_wol        = asix_get_wol,
    .set_wol        = asix_set_wol,
    .get_eeprom_len        = asix_get_eeprom_len,
    .get_eeprom        = asix_get_eeprom,
    .set_eeprom        = asix_set_eeprom,
    .nway_reset        = phy_ethtool_nway_reset,
    .get_link_ksettings    = phy_ethtool_get_link_ksettings,
    .set_link_ksettings    = phy_ethtool_set_link_ksettings,
    .self_test        = net_selftest,
    .get_strings        = ax88772_ethtool_get_strings,
    .get_sset_count        = ax88772_ethtool_get_sset_count,
    .get_pauseparam        = ax88772_ethtool_get_pauseparam,
    .set_pauseparam        = ax88772_ethtool_set_pauseparam,
};

static int ax88772_reset(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    struct asix_common_private *priv = dev->driver_priv;
    int ret;

    /* Rewrite MAC address */
    ether_addr_copy(data->mac_addr, dev->net->dev_addr);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
                 ETH_ALEN, data->mac_addr, 0);
    if (ret < 0)
        goto out;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
    if (ret < 0)
        goto out;

    ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0);
    if (ret < 0)
        goto out;

    phylink_start(priv->phylink);

    return 0;

out:
    return ret;
}

static int ax88772_hw_reset(struct usbnet *dev, int in_pm)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    struct asix_common_private *priv = dev->driver_priv;
    u16 rx_ctl;
    int ret;

    ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
                  AX_GPIO_GPO2EN, 5, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy,
                 0, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
        goto out;
    }

    if (priv->embd_phy) {
        ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm);
        if (ret < 0)
            goto out;

        usleep_range(10000, 11000);

        ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
        if (ret < 0)
            goto out;

        msleep(60);

        ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL,
                    in_pm);
        if (ret < 0)
            goto out;
    } else {
        ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL,
                    in_pm);
        if (ret < 0)
            goto out;
    }

    msleep(150);

    if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                       MII_PHYSID1))){
        ret = -EIO;
        goto out;
    }

    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
                 AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
                 AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
        goto out;
    }

    /* Rewrite MAC address */
    ether_addr_copy(data->mac_addr, dev->net->dev_addr);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
                 ETH_ALEN, data->mac_addr, in_pm);
    if (ret < 0)
        goto out;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    rx_ctl = asix_read_rx_ctl(dev, in_pm);
    netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
           rx_ctl);

    rx_ctl = asix_read_medium_status(dev, in_pm);
    netdev_dbg(dev->net,
           "Medium Status is 0x%04x after all initializations\n",
           rx_ctl);

    return 0;

out:
    return ret;
}

static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    struct asix_common_private *priv = dev->driver_priv;
    u16 rx_ctl, phy14h, phy15h, phy16h;
    int ret;

    ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy |
                 AX_PHYSEL_SSEN, 0, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
        goto out;
    }
    usleep_range(10000, 11000);

    ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm);
    if (ret < 0)
        goto out;

    usleep_range(10000, 11000);

    ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
    if (ret < 0)
        goto out;

    msleep(160);

    ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
    if (ret < 0)
        goto out;

    msleep(200);

    if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                       MII_PHYSID1))) {
        ret = -1;
        goto out;
    }

    if (priv->chipcode == AX_AX88772B_CHIPCODE) {
        ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001,
                     0, NULL, in_pm);
        if (ret < 0) {
            netdev_dbg(dev->net, "Write BQ setting failed: %d\n",
                   ret);
            goto out;
        }
    } else if (priv->chipcode == AX_AX88772A_CHIPCODE) {
        /* Check if the PHY registers have default settings */
        phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY14H);
        phy15h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY15H);
        phy16h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY16H);

        netdev_dbg(dev->net,
               "772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\n",
               phy14h, phy15h, phy16h);

        /* Restore PHY registers default setting if not */
        if (phy14h != AX88772A_PHY14H_DEFAULT)
            asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY14H,
                         AX88772A_PHY14H_DEFAULT);
        if (phy15h != AX88772A_PHY15H_DEFAULT)
            asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY15H,
                         AX88772A_PHY15H_DEFAULT);
        if (phy16h != AX88772A_PHY16H_DEFAULT)
            asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY16H,
                         AX88772A_PHY16H_DEFAULT);
    }

    ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
                AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
                AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
        goto out;
    }

    /* Rewrite MAC address */
    memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
                            data->mac_addr, in_pm);
    if (ret < 0)
        goto out;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
    if (ret < 0)
        return ret;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    rx_ctl = asix_read_rx_ctl(dev, in_pm);
    netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
           rx_ctl);

    rx_ctl = asix_read_medium_status(dev, in_pm);
    netdev_dbg(dev->net,
           "Medium Status is 0x%04x after all initializations\n",
           rx_ctl);

    return 0;

out:
    return ret;
}

static const struct net_device_ops ax88772_netdev_ops = {
    .ndo_open        = usbnet_open,
    .ndo_stop        = usbnet_stop,
    .ndo_start_xmit        = usbnet_start_xmit,
    .ndo_tx_timeout        = usbnet_tx_timeout,
    .ndo_change_mtu        = usbnet_change_mtu,
    .ndo_get_stats64    = dev_get_tstats64,
    .ndo_set_mac_address     = asix_set_mac_address,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_eth_ioctl        = phy_do_ioctl_running,
    .ndo_set_rx_mode        = asix_set_multicast,
};

static void ax88772_suspend(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    u16 medium;

    if (netif_running(dev->net)) {
        rtnl_lock();
        phylink_suspend(priv->phylink, false);
        rtnl_unlock();
    }

    /* Stop MAC operation */
    medium = asix_read_medium_status(dev, 1);
    medium &= ~AX_MEDIUM_RE;
    asix_write_medium_mode(dev, medium, 1);

    netdev_dbg(dev->net, "ax88772_suspend: medium=0x%04x\n",
           asix_read_medium_status(dev, 1));
}

static int asix_suspend(struct usb_interface *intf, pm_message_t message)
{
    struct usbnet *dev = usb_get_intfdata(intf);
    struct asix_common_private *priv = dev->driver_priv;

    if (priv && priv->suspend)
        priv->suspend(dev);

    return usbnet_suspend(intf, message);
}

static void ax88772_resume(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    int i;

    for (i = 0; i < 3; i++)
        if (!priv->reset(dev, 1))
            break;

    if (netif_running(dev->net)) {
        rtnl_lock();
        phylink_resume(priv->phylink);
        rtnl_unlock();
    }
}

static int asix_resume(struct usb_interface *intf)
{
    struct usbnet *dev = usb_get_intfdata(intf);
    struct asix_common_private *priv = dev->driver_priv;

    if (priv && priv->resume)
        priv->resume(dev);

    return usbnet_resume(intf);
}

static int ax88772_init_mdio(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;

    priv->mdio = devm_mdiobus_alloc(&dev->udev->dev);
    if (!priv->mdio)
        return -ENOMEM;

    priv->mdio->priv = dev;
    priv->mdio->read = &asix_mdio_bus_read;
    priv->mdio->write = &asix_mdio_bus_write;
    priv->mdio->name = "Asix MDIO Bus";
    /* mii bus name is usb-<usb bus number>-<usb device number> */
    snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
         dev->udev->bus->busnum, dev->udev->devnum);

    return devm_mdiobus_register(&dev->udev->dev, priv->mdio);
}

static int ax88772_init_phy(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    int ret;

    priv->phydev = mdiobus_get_phy(priv->mdio, priv->phy_addr);
    if (!priv->phydev) {
        netdev_err(dev->net, "Could not find PHY\n");
        return -ENODEV;
    }

    ret = phylink_connect_phy(priv->phylink, priv->phydev);
    if (ret) {
        netdev_err(dev->net, "Could not connect PHY\n");
        return ret;
    }

    phy_suspend(priv->phydev);
    priv->phydev->mac_managed_pm = 1;

    phy_attached_info(priv->phydev);

    if (priv->embd_phy)
        return 0;

    /* In case main PHY is not the embedded PHY and MAC is RMII clock
     * provider, we need to suspend embedded PHY by keeping PLL enabled
     * (AX_SWRESET_IPPD == 0).
     */
    priv->phydev_int = mdiobus_get_phy(priv->mdio, AX_EMBD_PHY_ADDR);
    if (!priv->phydev_int) {
        rtnl_lock();
        phylink_disconnect_phy(priv->phylink);
        rtnl_unlock();
        netdev_err(dev->net, "Could not find internal PHY\n");
        return -ENODEV;
    }

    priv->phydev_int->mac_managed_pm = 1;
    phy_suspend(priv->phydev_int);

    return 0;
}

static void ax88772_mac_config(struct phylink_config *config, unsigned int mode,
                  const struct phylink_link_state *state)
{
    /* Nothing to do */
}

static void ax88772_mac_link_down(struct phylink_config *config,
                 unsigned int mode, phy_interface_t interface)
{
    struct usbnet *dev = netdev_priv(to_net_dev(config->dev));

    asix_write_medium_mode(dev, 0, 0);
    usbnet_link_change(dev, false, false);
}

static void ax88772_mac_link_up(struct phylink_config *config,
                   struct phy_device *phy,
                   unsigned int mode, phy_interface_t interface,
                   int speed, int duplex,
                   bool tx_pause, bool rx_pause)
{
    struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
    u16 m = AX_MEDIUM_AC | AX_MEDIUM_RE;

    m |= duplex ? AX_MEDIUM_FD : 0;

    switch (speed) {
    case SPEED_100:
        m |= AX_MEDIUM_PS;
        break;
    case SPEED_10:
        break;
    default:
        return;
    }

    if (tx_pause)
        m |= AX_MEDIUM_TFC;

    if (rx_pause)
        m |= AX_MEDIUM_RFC;

    asix_write_medium_mode(dev, m, 0);
    usbnet_link_change(dev, true, false);
}

static const struct phylink_mac_ops ax88772_phylink_mac_ops = {
    .validate = phylink_generic_validate,
    .mac_config = ax88772_mac_config,
    .mac_link_down = ax88772_mac_link_down,
    .mac_link_up = ax88772_mac_link_up,
};

static int ax88772_phylink_setup(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    phy_interface_t phy_if_mode;
    struct phylink *phylink;

    priv->phylink_config.dev = &dev->net->dev;
    priv->phylink_config.type = PHYLINK_NETDEV;
    priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
        MAC_10 | MAC_100;

    __set_bit(PHY_INTERFACE_MODE_INTERNAL,
          priv->phylink_config.supported_interfaces);
    __set_bit(PHY_INTERFACE_MODE_RMII,
          priv->phylink_config.supported_interfaces);

    if (priv->embd_phy)
        phy_if_mode = PHY_INTERFACE_MODE_INTERNAL;
    else
        phy_if_mode = PHY_INTERFACE_MODE_RMII;

    phylink = phylink_create(&priv->phylink_config, dev->net->dev.fwnode,
                 phy_if_mode, &ax88772_phylink_mac_ops);
    if (IS_ERR(phylink))
        return PTR_ERR(phylink);

    priv->phylink = phylink;
    return 0;
}

static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
{
    struct asix_common_private *priv;
    u8 buf[ETH_ALEN] = {0};
    int ret, i;

    priv = devm_kzalloc(&dev->udev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    dev->driver_priv = priv;

    usbnet_get_endpoints(dev, intf);

    /* Maybe the boot loader passed the MAC address via device tree */
    if (!eth_platform_get_mac_address(&dev->udev->dev, buf)) {
        netif_dbg(dev, ifup, dev->net,
              "MAC address read from device tree");
    } else {
        /* Try getting the MAC address from EEPROM */
        if (dev->driver_info->data & FLAG_EEPROM_MAC) {
            for (i = 0; i < (ETH_ALEN >> 1); i++) {
                ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM,
                            0x04 + i, 0, 2, buf + i * 2,
                            0);
                if (ret < 0)
                    break;
            }
        } else {
            ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
                        0, 0, ETH_ALEN, buf, 0);
        }

        if (ret < 0) {
            netdev_dbg(dev->net, "Failed to read MAC address: %d\n",
                   ret);
            return ret;
        }
    }

    asix_set_netdev_dev_addr(dev, buf);

    dev->net->netdev_ops = &ax88772_netdev_ops;
    dev->net->ethtool_ops = &ax88772_ethtool_ops;
    dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
    dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */

    ret = asix_read_phy_addr(dev, true);
    if (ret < 0)
        return ret;

    priv->phy_addr = ret;
    priv->embd_phy = ((priv->phy_addr & 0x1f) == AX_EMBD_PHY_ADDR);

    ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1,
                &priv->chipcode, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read STATMNGSTS_REG: %d\n", ret);
        return ret;
    }

    priv->chipcode &= AX_CHIPCODE_MASK;

    priv->resume = ax88772_resume;
    priv->suspend = ax88772_suspend;
    if (priv->chipcode == AX_AX88772_CHIPCODE)
        priv->reset = ax88772_hw_reset;
    else
        priv->reset = ax88772a_hw_reset;

    ret = priv->reset(dev, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to reset AX88772: %d\n", ret);
        return ret;
    }

    /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
    if (dev->driver_info->flags & FLAG_FRAMING_AX) {
        /* hard_mtu  is still the default - the device does not support
           jumbo eth frames */
        dev->rx_urb_size = 2048;
    }

    priv->presvd_phy_bmcr = 0;
    priv->presvd_phy_advertise = 0;

    ret = ax88772_init_mdio(dev);
    if (ret)
        return ret;

    ret = ax88772_phylink_setup(dev);
    if (ret)
        return ret;

    ret = ax88772_init_phy(dev);
    if (ret)
        phylink_destroy(priv->phylink);

    return ret;
}

static int ax88772_stop(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;

    phylink_stop(priv->phylink);

    return 0;
}

static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
{
    struct asix_common_private *priv = dev->driver_priv;

    rtnl_lock();
    phylink_disconnect_phy(priv->phylink);
    rtnl_unlock();
    phylink_destroy(priv->phylink);
    asix_rx_fixup_common_free(dev->driver_priv);
}

static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf)
{
    asix_rx_fixup_common_free(dev->driver_priv);
    kfree(dev->driver_priv);
}

static const struct ethtool_ops ax88178_ethtool_ops = {
    .get_drvinfo        = asix_get_drvinfo,
    .get_link        = asix_get_link,
    .get_msglevel        = usbnet_get_msglevel,
    .set_msglevel        = usbnet_set_msglevel,
    .get_wol        = asix_get_wol,
    .set_wol        = asix_set_wol,
    .get_eeprom_len        = asix_get_eeprom_len,
    .get_eeprom        = asix_get_eeprom,
    .set_eeprom        = asix_set_eeprom,
    .nway_reset        = usbnet_nway_reset,
    .get_link_ksettings    = usbnet_get_link_ksettings_mii,
    .set_link_ksettings    = usbnet_set_link_ksettings_mii,
};

static int marvell_phy_init(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    u16 reg;

    netdev_dbg(dev->net, "marvell_phy_init()\n");

    reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS);
    netdev_dbg(dev->net, "MII_MARVELL_STATUS = 0x%04x\n", reg);

    asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL,
            MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY);

    if (data->ledmode) {
        reg = asix_mdio_read(dev->net, dev->mii.phy_id,
            MII_MARVELL_LED_CTRL);
        netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (1) = 0x%04x\n", reg);

        reg &= 0xf8ff;
        reg |= (1 + 0x0100);
        asix_mdio_write(dev->net, dev->mii.phy_id,
            MII_MARVELL_LED_CTRL, reg);

        reg = asix_mdio_read(dev->net, dev->mii.phy_id,
            MII_MARVELL_LED_CTRL);
        netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg);
    }

    return 0;
}

static int rtl8211cl_phy_init(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;

    netdev_dbg(dev->net, "rtl8211cl_phy_init()\n");

    asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0005);
    asix_mdio_write (dev->net, dev->mii.phy_id, 0x0c, 0);
    asix_mdio_write (dev->net, dev->mii.phy_id, 0x01,
        asix_mdio_read (dev->net, dev->mii.phy_id, 0x01) | 0x0080);
    asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);

    if (data->ledmode == 12) {
        asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0002);
        asix_mdio_write (dev->net, dev->mii.phy_id, 0x1a, 0x00cb);
        asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);
    }

    return 0;
}

static int marvell_led_status(struct usbnet *dev, u16 speed)
{
    u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);

    netdev_dbg(dev->net, "marvell_led_status() read 0x%04x\n", reg);

    /* Clear out the center LED bits - 0x03F0 */
    reg &= 0xfc0f;

    switch (speed) {
        case SPEED_1000:
            reg |= 0x03e0;
            break;
        case SPEED_100:
            reg |= 0x03b0;
            break;
        default:
            reg |= 0x02f0;
    }

    netdev_dbg(dev->net, "marvell_led_status() writing 0x%04x\n", reg);
    asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg);

    return 0;
}

static int ax88178_reset(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    int ret;
    __le16 eeprom;
    u8 status;
    int gpio0 = 0;
    u32 phyid;

    ret = asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read GPIOS: %d\n", ret);
        return ret;
    }

    netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status);

    asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0);
    ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read EEPROM: %d\n", ret);
        return ret;
    }

    asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0);

    netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom);

    if (eeprom == cpu_to_le16(0xffff)) {
        data->phymode = PHY_MODE_MARVELL;
        data->ledmode = 0;
        gpio0 = 1;
    } else {
        data->phymode = le16_to_cpu(eeprom) & 0x7F;
        data->ledmode = le16_to_cpu(eeprom) >> 8;
        gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1;
    }
    netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode);

    /* Power up external GigaPHY through AX88178 GPIO pin */
    asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 |
            AX_GPIO_GPO1EN, 40, 0);
    if ((le16_to_cpu(eeprom) >> 8) != 1) {
        asix_write_gpio(dev, 0x003c, 30, 0);
        asix_write_gpio(dev, 0x001c, 300, 0);
        asix_write_gpio(dev, 0x003c, 30, 0);
    } else {
        netdev_dbg(dev->net, "gpio phymode == 1 path\n");
        asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0);
        asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0);
    }

    /* Read PHYID register *AFTER* powering up PHY */
    phyid = asix_get_phyid(dev);
    netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);

    /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
    asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0);

    asix_sw_reset(dev, 0, 0);
    msleep(150);

    asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
    msleep(150);

    asix_write_rx_ctl(dev, 0, 0);

    if (data->phymode == PHY_MODE_MARVELL) {
        marvell_phy_init(dev);
        msleep(60);
    } else if (data->phymode == PHY_MODE_RTL8211CL)
        rtl8211cl_phy_init(dev);

    asix_phy_reset(dev, BMCR_RESET | BMCR_ANENABLE);
    asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
            ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
    asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
            ADVERTISE_1000FULL);

    asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0);
    mii_nway_restart(&dev->mii);

    /* Rewrite MAC address */
    memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
                            data->mac_addr, 0);
    if (ret < 0)
        return ret;

    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
    if (ret < 0)
        return ret;

    return 0;
}

static int ax88178_link_reset(struct usbnet *dev)
{
    u16 mode;
    struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
    struct asix_data *data = (struct asix_data *)&dev->data;
    u32 speed;

    netdev_dbg(dev->net, "ax88178_link_reset()\n");

    mii_check_media(&dev->mii, 1, 1);
    mii_ethtool_gset(&dev->mii, &ecmd);
    mode = AX88178_MEDIUM_DEFAULT;
    speed = ethtool_cmd_speed(&ecmd);

    if (speed == SPEED_1000)
        mode |= AX_MEDIUM_GM;
    else if (speed == SPEED_100)
        mode |= AX_MEDIUM_PS;
    else
        mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM);

    mode |= AX_MEDIUM_ENCK;

    if (ecmd.duplex == DUPLEX_FULL)
        mode |= AX_MEDIUM_FD;
    else
        mode &= ~AX_MEDIUM_FD;

    netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
           speed, ecmd.duplex, mode);

    asix_write_medium_mode(dev, mode, 0);

    if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
        marvell_led_status(dev, speed);

    return 0;
}

static void ax88178_set_mfb(struct usbnet *dev)
{
    u16 mfb = AX_RX_CTL_MFB_16384;
    u16 rxctl;
    u16 medium;
    int old_rx_urb_size = dev->rx_urb_size;

    if (dev->hard_mtu < 2048) {
        dev->rx_urb_size = 2048;
        mfb = AX_RX_CTL_MFB_2048;
    } else if (dev->hard_mtu < 4096) {
        dev->rx_urb_size = 4096;
        mfb = AX_RX_CTL_MFB_4096;
    } else if (dev->hard_mtu < 8192) {
        dev->rx_urb_size = 8192;
        mfb = AX_RX_CTL_MFB_8192;
    } else if (dev->hard_mtu < 16384) {
        dev->rx_urb_size = 16384;
        mfb = AX_RX_CTL_MFB_16384;
    }

    rxctl = asix_read_rx_ctl(dev, 0);
    asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0);

    medium = asix_read_medium_status(dev, 0);
    if (dev->net->mtu > 1500)
        medium |= AX_MEDIUM_JFE;
    else
        medium &= ~AX_MEDIUM_JFE;
    asix_write_medium_mode(dev, medium, 0);

    if (dev->rx_urb_size > old_rx_urb_size)
        usbnet_unlink_rx_urbs(dev);
}

static int ax88178_change_mtu(struct net_device *net, int new_mtu)
{
    struct usbnet *dev = netdev_priv(net);
    int ll_mtu = new_mtu + net->hard_header_len + 4;

    netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu);

    if ((ll_mtu % dev->maxpacket) == 0)
        return -EDOM;

    net->mtu = new_mtu;
    dev->hard_mtu = net->mtu + net->hard_header_len;
    ax88178_set_mfb(dev);

    /* max qlen depend on hard_mtu and rx_urb_size */
    usbnet_update_max_qlen(dev);

    return 0;
}

static const struct net_device_ops ax88178_netdev_ops = {
    .ndo_open        = usbnet_open,
    .ndo_stop        = usbnet_stop,
    .ndo_start_xmit        = usbnet_start_xmit,
    .ndo_tx_timeout        = usbnet_tx_timeout,
    .ndo_get_stats64    = dev_get_tstats64,
    .ndo_set_mac_address     = asix_set_mac_address,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_set_rx_mode    = asix_set_multicast,
    .ndo_eth_ioctl        = asix_ioctl,
    .ndo_change_mtu     = ax88178_change_mtu,
};

static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
{
    int ret;
    u8 buf[ETH_ALEN] = {0};

    usbnet_get_endpoints(dev,intf);

    /* Get the MAC address */
    ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret);
        return ret;
    }

    asix_set_netdev_dev_addr(dev, buf);

    /* Initialize MII structure */
    dev->mii.dev = dev->net;
    dev->mii.mdio_read = asix_mdio_read;
    dev->mii.mdio_write = asix_mdio_write;
    dev->mii.phy_id_mask = 0x1f;
    dev->mii.reg_num_mask = 0xff;
    dev->mii.supports_gmii = 1;

    dev->mii.phy_id = asix_read_phy_addr(dev, true);
    if (dev->mii.phy_id < 0)
        return dev->mii.phy_id;

    dev->net->netdev_ops = &ax88178_netdev_ops;
    dev->net->ethtool_ops = &ax88178_ethtool_ops;
    dev->net->max_mtu = 16384 - (dev->net->hard_header_len + 4);

    /* Blink LEDS so users know driver saw dongle */
    asix_sw_reset(dev, 0, 0);
    msleep(150);

    asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
    msleep(150);

    /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
    if (dev->driver_info->flags & FLAG_FRAMING_AX) {
        /* hard_mtu  is still the default - the device does not support
           jumbo eth frames */
        dev->rx_urb_size = 2048;
    }

    dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
    if (!dev->driver_priv)
            return -ENOMEM;

    return 0;
}

static const struct driver_info ax8817x_info = {
    .description = "ASIX AX8817x USB 2.0 Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x00130103,
};

static const struct driver_info dlink_dub_e100_info = {
    .description = "DLink DUB-E100 USB Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x009f9d9f,
};

static const struct driver_info netgear_fa120_info = {
    .description = "Netgear FA-120 USB Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x00130103,
};

static const struct driver_info hawking_uf200_info = {
    .description = "Hawking UF200 USB Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x001f1d1f,
};

static const struct driver_info ax88772_info = {
    .description = "ASIX AX88772 USB 2.0 Ethernet",
    .bind = ax88772_bind,
    .unbind = ax88772_unbind,
    .status = asix_status,
    .reset = ax88772_reset,
    .stop = ax88772_stop,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
};

static const struct driver_info ax88772b_info = {
    .description = "ASIX AX88772B USB 2.0 Ethernet",
    .bind = ax88772_bind,
    .unbind = ax88772_unbind,
    .status = asix_status,
    .reset = ax88772_reset,
    .stop = ax88772_stop,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
             FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
    .data = FLAG_EEPROM_MAC,
};

static const struct driver_info ax88178_info = {
    .description = "ASIX AX88178 USB 2.0 Ethernet",
    .bind = ax88178_bind,
    .unbind = ax88178_unbind,
    .status = asix_status,
    .link_reset = ax88178_link_reset,
    .reset = ax88178_reset,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
         FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
};

/*
 * USBLINK 20F9 "USB 2.0 LAN" USB ethernet adapter, typically found in
 * no-name packaging.
 * USB device strings are:
 *   1: Manufacturer: USBLINK
 *   2: Product: HG20F9 USB2.0
 *   3: Serial: 000003
 * Appears to be compatible with Asix 88772B.
 */
static const struct driver_info hg20f9_info = {
    .description = "HG20F9 USB 2.0 Ethernet",
    .bind = ax88772_bind,
    .unbind = ax88772_unbind,
    .status = asix_status,
    .reset = ax88772_reset,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
             FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
    .data = FLAG_EEPROM_MAC,
};

static const struct usb_device_id    products [] = {
{
    // Linksys USB200M
    USB_DEVICE (0x077b, 0x2226),
    .driver_info =    (unsigned long) &ax8817x_info,
}, {
    // Netgear FA120
    USB_DEVICE (0x0846, 0x1040),
    .driver_info =  (unsigned long) &netgear_fa120_info,
}, {
    // DLink DUB-E100
    USB_DEVICE (0x2001, 0x1a00),
    .driver_info =  (unsigned long) &dlink_dub_e100_info,
}, {
    // Intellinet, ST Lab USB Ethernet
    USB_DEVICE (0x0b95, 0x1720),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Hawking UF200, TrendNet TU2-ET100
    USB_DEVICE (0x07b8, 0x420a),
    .driver_info =  (unsigned long) &hawking_uf200_info,
}, {
    // Billionton Systems, USB2AR
    USB_DEVICE (0x08dd, 0x90ff),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Billionton Systems, GUSB2AM-1G-B
    USB_DEVICE(0x08dd, 0x0114),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // ATEN UC210T
    USB_DEVICE (0x0557, 0x2009),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Buffalo LUA-U2-KTX
    USB_DEVICE (0x0411, 0x003d),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Buffalo LUA-U2-GT 10/100/1000
    USB_DEVICE (0x0411, 0x006e),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
    USB_DEVICE (0x6189, 0x182d),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter"
    USB_DEVICE (0x0df6, 0x0056),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter"
    USB_DEVICE (0x0df6, 0x061c),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // corega FEther USB2-TX
    USB_DEVICE (0x07aa, 0x0017),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Surecom EP-1427X-2
    USB_DEVICE (0x1189, 0x0893),
    .driver_info = (unsigned long) &ax8817x_info,
}, {
    // goodway corp usb gwusb2e
    USB_DEVICE (0x1631, 0x6200),
    .driver_info = (unsigned long) &ax8817x_info,
}, {
    // JVC MP-PRX1 Port Replicator
    USB_DEVICE (0x04f1, 0x3008),
    .driver_info = (unsigned long) &ax8817x_info,
}, {
    // Lenovo U2L100P 10/100
    USB_DEVICE (0x17ef, 0x7203),
    .driver_info = (unsigned long)&ax88772b_info,
}, {
    // ASIX AX88772B 10/100
    USB_DEVICE (0x0b95, 0x772b),
    .driver_info = (unsigned long) &ax88772b_info,
}, {
    // ASIX AX88772 10/100
    USB_DEVICE (0x0b95, 0x7720),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // ASIX AX88178 10/100/1000
    USB_DEVICE (0x0b95, 0x1780),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Logitec LAN-GTJ/U2A
    USB_DEVICE (0x0789, 0x0160),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Linksys USB200M Rev 2
    USB_DEVICE (0x13b1, 0x0018),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // 0Q0 cable ethernet
    USB_DEVICE (0x1557, 0x7720),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // DLink DUB-E100 H/W Ver B1
    USB_DEVICE (0x07d1, 0x3c05),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // DLink DUB-E100 H/W Ver B1 Alternate
    USB_DEVICE (0x2001, 0x3c05),
    .driver_info = (unsigned long) &ax88772_info,
}, {
       // DLink DUB-E100 H/W Ver C1
       USB_DEVICE (0x2001, 0x1a02),
       .driver_info = (unsigned long) &ax88772_info,
}, {
    // Linksys USB1000
    USB_DEVICE (0x1737, 0x0039),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // IO-DATA ETG-US2
    USB_DEVICE (0x04bb, 0x0930),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Belkin F5D5055
    USB_DEVICE(0x050d, 0x5055),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Apple USB Ethernet Adapter
    USB_DEVICE(0x05ac, 0x1402),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // Cables-to-Go USB Ethernet Adapter
    USB_DEVICE(0x0b95, 0x772a),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // ABOCOM for pci
    USB_DEVICE(0x14ea, 0xab11),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // ASIX 88772a
    USB_DEVICE(0x0db0, 0xa877),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // Asus USB Ethernet Adapter
    USB_DEVICE (0x0b95, 0x7e2b),
    .driver_info = (unsigned long)&ax88772b_info,
}, {
    /* ASIX 88172a demo board */
    USB_DEVICE(0x0b95, 0x172a),
    .driver_info = (unsigned long) &ax88172a_info,
}, {
    /*
     * USBLINK HG20F9 "USB 2.0 LAN"
     * Appears to have gazumped Linksys's manufacturer ID but
     * doesn't (yet) conflict with any known Linksys product.
     */
    USB_DEVICE(0x066b, 0x20f9),
    .driver_info = (unsigned long) &hg20f9_info,
},
    { },        // END
};
MODULE_DEVICE_TABLE(usb, products);

static struct usb_driver asix_driver = {
    .name =        DRIVER_NAME,
    .id_table =    products,
    .probe =    usbnet_probe,
    .suspend =    asix_suspend,
    .resume =    asix_resume,
    .reset_resume =    asix_resume,
    .disconnect =    usbnet_disconnect,
    .supports_autosuspend = 1,
    .disable_hub_initiated_lpm = 1,
};

module_usb_driver(asix_driver);

MODULE_AUTHOR("David Hollis");
MODULE_VERSION(DRIVER_VERSION);
MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices");
MODULE_LICENSE("GPL");
 

Chainhunter

Active Member
Newcomer
Joined
May 29, 2008
Messages
25
Trophies
1
XP
1,297
Country
C:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * ASIX AX8817X based USB 2.0 Ethernet Devices
 * Copyright (C) 2003-2006 David Hollis <[email protected]>
 * Copyright (C) 2005 Phil Chang <[email protected]>
 * Copyright (C) 2006 James Painter <[email protected]>
 * Copyright (c) 2002-2003 TiVo Inc.
 */

#include "asix.h"

#define PHY_MODE_MARVELL    0x0000
#define MII_MARVELL_LED_CTRL    0x0018
#define MII_MARVELL_STATUS    0x001b
#define MII_MARVELL_CTRL    0x0014

#define MARVELL_LED_MANUAL    0x0019

#define MARVELL_STATUS_HWCFG    0x0004

#define MARVELL_CTRL_TXDELAY    0x0002
#define MARVELL_CTRL_RXDELAY    0x0080

#define    PHY_MODE_RTL8211CL    0x000C

#define AX88772A_PHY14H        0x14
#define AX88772A_PHY14H_DEFAULT 0x442C

#define AX88772A_PHY15H        0x15
#define AX88772A_PHY15H_DEFAULT 0x03C8

#define AX88772A_PHY16H        0x16
#define AX88772A_PHY16H_DEFAULT 0x4044

struct ax88172_int_data {
    __le16 res1;
    u8 link;
    __le16 res2;
    u8 status;
    __le16 res3;
} __packed;

static void asix_status(struct usbnet *dev, struct urb *urb)
{
    struct ax88172_int_data *event;
    int link;

    if (urb->actual_length < 8)
        return;

    event = urb->transfer_buffer;
    link = event->link & 0x01;
    if (netif_carrier_ok(dev->net) != link) {
        usbnet_link_change(dev, link, 1);
        netdev_dbg(dev->net, "Link Status is: %d\n", link);
    }
}

static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr)
{
    if (is_valid_ether_addr(addr)) {
        eth_hw_addr_set(dev->net, addr);
    } else {
        netdev_info(dev->net, "invalid hw address, using random\n");
        eth_hw_addr_random(dev->net);
    }
}

/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
static u32 asix_get_phyid(struct usbnet *dev)
{
    int phy_reg;
    u32 phy_id;
    int i;

    /* Poll for the rare case the FW or phy isn't ready yet.  */
    for (i = 0; i < 100; i++) {
        phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
        if (phy_reg < 0)
            return 0;
        if (phy_reg != 0 && phy_reg != 0xFFFF)
            break;
        mdelay(1);
    }

    if (phy_reg <= 0 || phy_reg == 0xFFFF)
        return 0;

    phy_id = (phy_reg & 0xffff) << 16;

    phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2);
    if (phy_reg < 0)
        return 0;

    phy_id |= (phy_reg & 0xffff);

    return phy_id;
}

static u32 asix_get_link(struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);

    return mii_link_ok(&dev->mii);
}

static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
{
    struct usbnet *dev = netdev_priv(net);

    return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
}

/* We need to override some ethtool_ops so we require our
   own structure so we don't interfere with other usbnet
   devices that may be connected at the same time. */
static const struct ethtool_ops ax88172_ethtool_ops = {
    .get_drvinfo        = asix_get_drvinfo,
    .get_link        = asix_get_link,
    .get_msglevel        = usbnet_get_msglevel,
    .set_msglevel        = usbnet_set_msglevel,
    .get_wol        = asix_get_wol,
    .set_wol        = asix_set_wol,
    .get_eeprom_len        = asix_get_eeprom_len,
    .get_eeprom        = asix_get_eeprom,
    .set_eeprom        = asix_set_eeprom,
    .nway_reset        = usbnet_nway_reset,
    .get_link_ksettings    = usbnet_get_link_ksettings_mii,
    .set_link_ksettings    = usbnet_set_link_ksettings_mii,
};

static void ax88172_set_multicast(struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);
    struct asix_data *data = (struct asix_data *)&dev->data;
    u8 rx_ctl = 0x8c;

    if (net->flags & IFF_PROMISC) {
        rx_ctl |= 0x01;
    } else if (net->flags & IFF_ALLMULTI ||
           netdev_mc_count(net) > AX_MAX_MCAST) {
        rx_ctl |= 0x02;
    } else if (netdev_mc_empty(net)) {
        /* just broadcast and directed */
    } else {
        /* We use the 20 byte dev->data
         * for our 8 byte filter buffer
         * to avoid allocating memory that
         * is tricky to free later */
        struct netdev_hw_addr *ha;
        u32 crc_bits;

        memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);

        /* Build the multicast hash filter. */
        netdev_for_each_mc_addr(ha, net) {
            crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
            data->multi_filter[crc_bits >> 3] |=
                1 << (crc_bits & 7);
        }

        asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
                   AX_MCAST_FILTER_SIZE, data->multi_filter);

        rx_ctl |= 0x10;
    }

    asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
}

static int ax88172_link_reset(struct usbnet *dev)
{
    u8 mode;
    struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };

    mii_check_media(&dev->mii, 1, 1);
    mii_ethtool_gset(&dev->mii, &ecmd);
    mode = AX88172_MEDIUM_DEFAULT;

    if (ecmd.duplex != DUPLEX_FULL)
        mode |= ~AX88172_MEDIUM_FD;

    netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
           ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);

    asix_write_medium_mode(dev, mode, 0);

    return 0;
}

static const struct net_device_ops ax88172_netdev_ops = {
    .ndo_open        = usbnet_open,
    .ndo_stop        = usbnet_stop,
    .ndo_start_xmit        = usbnet_start_xmit,
    .ndo_tx_timeout        = usbnet_tx_timeout,
    .ndo_change_mtu        = usbnet_change_mtu,
    .ndo_get_stats64    = dev_get_tstats64,
    .ndo_set_mac_address     = eth_mac_addr,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_eth_ioctl        = asix_ioctl,
    .ndo_set_rx_mode    = ax88172_set_multicast,
};

static void asix_phy_reset(struct usbnet *dev, unsigned int reset_bits)
{
    unsigned int timeout = 5000;

    asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, reset_bits);

    /* give phy_id a chance to process reset */
    udelay(500);

    /* See IEEE 802.3 "22.2.4.1.1 Reset": 500ms max */
    while (timeout--) {
        if (asix_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR)
                            & BMCR_RESET)
            udelay(100);
        else
            return;
    }

    netdev_err(dev->net, "BMCR_RESET timeout on phy_id %d\n",
           dev->mii.phy_id);
}

static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
{
    int ret = 0;
    u8 buf[ETH_ALEN] = {0};
    int i;
    unsigned long gpio_bits = dev->driver_info->data;

    usbnet_get_endpoints(dev,intf);

    /* Toggle the GPIOs in a manufacturer/model specific way */
    for (i = 2; i >= 0; i--) {
        ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
                (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0);
        if (ret < 0)
            goto out;
        msleep(5);
    }

    ret = asix_write_rx_ctl(dev, 0x80, 0);
    if (ret < 0)
        goto out;

    /* Get the MAC address */
    ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
                0, 0, ETH_ALEN, buf, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n",
               ret);
        goto out;
    }

    asix_set_netdev_dev_addr(dev, buf);

    /* Initialize MII structure */
    dev->mii.dev = dev->net;
    dev->mii.mdio_read = asix_mdio_read;
    dev->mii.mdio_write = asix_mdio_write;
    dev->mii.phy_id_mask = 0x3f;
    dev->mii.reg_num_mask = 0x1f;

    dev->mii.phy_id = asix_read_phy_addr(dev, true);
    if (dev->mii.phy_id < 0)
        return dev->mii.phy_id;

    dev->net->netdev_ops = &ax88172_netdev_ops;
    dev->net->ethtool_ops = &ax88172_ethtool_ops;
    dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
    dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */

    asix_phy_reset(dev, BMCR_RESET);
    asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
        ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
    mii_nway_restart(&dev->mii);

    return 0;

out:
    return ret;
}

static void ax88772_ethtool_get_strings(struct net_device *netdev, u32 sset,
                    u8 *data)
{
    switch (sset) {
    case ETH_SS_TEST:
        net_selftest_get_strings(data);
        break;
    }
}

static int ax88772_ethtool_get_sset_count(struct net_device *ndev, int sset)
{
    switch (sset) {
    case ETH_SS_TEST:
        return net_selftest_get_count();
    default:
        return -EOPNOTSUPP;
    }
}

static void ax88772_ethtool_get_pauseparam(struct net_device *ndev,
                      struct ethtool_pauseparam *pause)
{
    struct usbnet *dev = netdev_priv(ndev);
    struct asix_common_private *priv = dev->driver_priv;

    phylink_ethtool_get_pauseparam(priv->phylink, pause);
}

static int ax88772_ethtool_set_pauseparam(struct net_device *ndev,
                     struct ethtool_pauseparam *pause)
{
    struct usbnet *dev = netdev_priv(ndev);
    struct asix_common_private *priv = dev->driver_priv;

    return phylink_ethtool_set_pauseparam(priv->phylink, pause);
}

static const struct ethtool_ops ax88772_ethtool_ops = {
    .get_drvinfo        = asix_get_drvinfo,
    .get_link        = usbnet_get_link,
    .get_msglevel        = usbnet_get_msglevel,
    .set_msglevel        = usbnet_set_msglevel,
    .get_wol        = asix_get_wol,
    .set_wol        = asix_set_wol,
    .get_eeprom_len        = asix_get_eeprom_len,
    .get_eeprom        = asix_get_eeprom,
    .set_eeprom        = asix_set_eeprom,
    .nway_reset        = phy_ethtool_nway_reset,
    .get_link_ksettings    = phy_ethtool_get_link_ksettings,
    .set_link_ksettings    = phy_ethtool_set_link_ksettings,
    .self_test        = net_selftest,
    .get_strings        = ax88772_ethtool_get_strings,
    .get_sset_count        = ax88772_ethtool_get_sset_count,
    .get_pauseparam        = ax88772_ethtool_get_pauseparam,
    .set_pauseparam        = ax88772_ethtool_set_pauseparam,
};

static int ax88772_reset(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    struct asix_common_private *priv = dev->driver_priv;
    int ret;

    /* Rewrite MAC address */
    ether_addr_copy(data->mac_addr, dev->net->dev_addr);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
                 ETH_ALEN, data->mac_addr, 0);
    if (ret < 0)
        goto out;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
    if (ret < 0)
        goto out;

    ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0);
    if (ret < 0)
        goto out;

    phylink_start(priv->phylink);

    return 0;

out:
    return ret;
}

static int ax88772_hw_reset(struct usbnet *dev, int in_pm)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    struct asix_common_private *priv = dev->driver_priv;
    u16 rx_ctl;
    int ret;

    ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
                  AX_GPIO_GPO2EN, 5, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy,
                 0, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
        goto out;
    }

    if (priv->embd_phy) {
        ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm);
        if (ret < 0)
            goto out;

        usleep_range(10000, 11000);

        ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
        if (ret < 0)
            goto out;

        msleep(60);

        ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL,
                    in_pm);
        if (ret < 0)
            goto out;
    } else {
        ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL,
                    in_pm);
        if (ret < 0)
            goto out;
    }

    msleep(150);

    if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                       MII_PHYSID1))){
        ret = -EIO;
        goto out;
    }

    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
                 AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
                 AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
        goto out;
    }

    /* Rewrite MAC address */
    ether_addr_copy(data->mac_addr, dev->net->dev_addr);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
                 ETH_ALEN, data->mac_addr, in_pm);
    if (ret < 0)
        goto out;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    rx_ctl = asix_read_rx_ctl(dev, in_pm);
    netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
           rx_ctl);

    rx_ctl = asix_read_medium_status(dev, in_pm);
    netdev_dbg(dev->net,
           "Medium Status is 0x%04x after all initializations\n",
           rx_ctl);

    return 0;

out:
    return ret;
}

static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    struct asix_common_private *priv = dev->driver_priv;
    u16 rx_ctl, phy14h, phy15h, phy16h;
    int ret;

    ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, priv->embd_phy |
                 AX_PHYSEL_SSEN, 0, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
        goto out;
    }
    usleep_range(10000, 11000);

    ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm);
    if (ret < 0)
        goto out;

    usleep_range(10000, 11000);

    ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
    if (ret < 0)
        goto out;

    msleep(160);

    ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
    if (ret < 0)
        goto out;

    msleep(200);

    if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                       MII_PHYSID1))) {
        ret = -1;
        goto out;
    }

    if (priv->chipcode == AX_AX88772B_CHIPCODE) {
        ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001,
                     0, NULL, in_pm);
        if (ret < 0) {
            netdev_dbg(dev->net, "Write BQ setting failed: %d\n",
                   ret);
            goto out;
        }
    } else if (priv->chipcode == AX_AX88772A_CHIPCODE) {
        /* Check if the PHY registers have default settings */
        phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY14H);
        phy15h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY15H);
        phy16h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY16H);

        netdev_dbg(dev->net,
               "772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\n",
               phy14h, phy15h, phy16h);

        /* Restore PHY registers default setting if not */
        if (phy14h != AX88772A_PHY14H_DEFAULT)
            asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY14H,
                         AX88772A_PHY14H_DEFAULT);
        if (phy15h != AX88772A_PHY15H_DEFAULT)
            asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY15H,
                         AX88772A_PHY15H_DEFAULT);
        if (phy16h != AX88772A_PHY16H_DEFAULT)
            asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
                         AX88772A_PHY16H,
                         AX88772A_PHY16H_DEFAULT);
    }

    ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
                AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
                AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
    if (ret < 0) {
        netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
        goto out;
    }

    /* Rewrite MAC address */
    memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
                            data->mac_addr, in_pm);
    if (ret < 0)
        goto out;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
    if (ret < 0)
        return ret;

    /* Set RX_CTL to default values with 2k buffer, and enable cactus */
    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
    if (ret < 0)
        goto out;

    rx_ctl = asix_read_rx_ctl(dev, in_pm);
    netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
           rx_ctl);

    rx_ctl = asix_read_medium_status(dev, in_pm);
    netdev_dbg(dev->net,
           "Medium Status is 0x%04x after all initializations\n",
           rx_ctl);

    return 0;

out:
    return ret;
}

static const struct net_device_ops ax88772_netdev_ops = {
    .ndo_open        = usbnet_open,
    .ndo_stop        = usbnet_stop,
    .ndo_start_xmit        = usbnet_start_xmit,
    .ndo_tx_timeout        = usbnet_tx_timeout,
    .ndo_change_mtu        = usbnet_change_mtu,
    .ndo_get_stats64    = dev_get_tstats64,
    .ndo_set_mac_address     = asix_set_mac_address,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_eth_ioctl        = phy_do_ioctl_running,
    .ndo_set_rx_mode        = asix_set_multicast,
};

static void ax88772_suspend(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    u16 medium;

    if (netif_running(dev->net)) {
        rtnl_lock();
        phylink_suspend(priv->phylink, false);
        rtnl_unlock();
    }

    /* Stop MAC operation */
    medium = asix_read_medium_status(dev, 1);
    medium &= ~AX_MEDIUM_RE;
    asix_write_medium_mode(dev, medium, 1);

    netdev_dbg(dev->net, "ax88772_suspend: medium=0x%04x\n",
           asix_read_medium_status(dev, 1));
}

static int asix_suspend(struct usb_interface *intf, pm_message_t message)
{
    struct usbnet *dev = usb_get_intfdata(intf);
    struct asix_common_private *priv = dev->driver_priv;

    if (priv && priv->suspend)
        priv->suspend(dev);

    return usbnet_suspend(intf, message);
}

static void ax88772_resume(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    int i;

    for (i = 0; i < 3; i++)
        if (!priv->reset(dev, 1))
            break;

    if (netif_running(dev->net)) {
        rtnl_lock();
        phylink_resume(priv->phylink);
        rtnl_unlock();
    }
}

static int asix_resume(struct usb_interface *intf)
{
    struct usbnet *dev = usb_get_intfdata(intf);
    struct asix_common_private *priv = dev->driver_priv;

    if (priv && priv->resume)
        priv->resume(dev);

    return usbnet_resume(intf);
}

static int ax88772_init_mdio(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;

    priv->mdio = devm_mdiobus_alloc(&dev->udev->dev);
    if (!priv->mdio)
        return -ENOMEM;

    priv->mdio->priv = dev;
    priv->mdio->read = &asix_mdio_bus_read;
    priv->mdio->write = &asix_mdio_bus_write;
    priv->mdio->name = "Asix MDIO Bus";
    /* mii bus name is usb-<usb bus number>-<usb device number> */
    snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
         dev->udev->bus->busnum, dev->udev->devnum);

    return devm_mdiobus_register(&dev->udev->dev, priv->mdio);
}

static int ax88772_init_phy(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    int ret;

    priv->phydev = mdiobus_get_phy(priv->mdio, priv->phy_addr);
    if (!priv->phydev) {
        netdev_err(dev->net, "Could not find PHY\n");
        return -ENODEV;
    }

    ret = phylink_connect_phy(priv->phylink, priv->phydev);
    if (ret) {
        netdev_err(dev->net, "Could not connect PHY\n");
        return ret;
    }

    phy_suspend(priv->phydev);
    priv->phydev->mac_managed_pm = 1;

    phy_attached_info(priv->phydev);

    if (priv->embd_phy)
        return 0;

    /* In case main PHY is not the embedded PHY and MAC is RMII clock
     * provider, we need to suspend embedded PHY by keeping PLL enabled
     * (AX_SWRESET_IPPD == 0).
     */
    priv->phydev_int = mdiobus_get_phy(priv->mdio, AX_EMBD_PHY_ADDR);
    if (!priv->phydev_int) {
        rtnl_lock();
        phylink_disconnect_phy(priv->phylink);
        rtnl_unlock();
        netdev_err(dev->net, "Could not find internal PHY\n");
        return -ENODEV;
    }

    priv->phydev_int->mac_managed_pm = 1;
    phy_suspend(priv->phydev_int);

    return 0;
}

static void ax88772_mac_config(struct phylink_config *config, unsigned int mode,
                  const struct phylink_link_state *state)
{
    /* Nothing to do */
}

static void ax88772_mac_link_down(struct phylink_config *config,
                 unsigned int mode, phy_interface_t interface)
{
    struct usbnet *dev = netdev_priv(to_net_dev(config->dev));

    asix_write_medium_mode(dev, 0, 0);
    usbnet_link_change(dev, false, false);
}

static void ax88772_mac_link_up(struct phylink_config *config,
                   struct phy_device *phy,
                   unsigned int mode, phy_interface_t interface,
                   int speed, int duplex,
                   bool tx_pause, bool rx_pause)
{
    struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
    u16 m = AX_MEDIUM_AC | AX_MEDIUM_RE;

    m |= duplex ? AX_MEDIUM_FD : 0;

    switch (speed) {
    case SPEED_100:
        m |= AX_MEDIUM_PS;
        break;
    case SPEED_10:
        break;
    default:
        return;
    }

    if (tx_pause)
        m |= AX_MEDIUM_TFC;

    if (rx_pause)
        m |= AX_MEDIUM_RFC;

    asix_write_medium_mode(dev, m, 0);
    usbnet_link_change(dev, true, false);
}

static const struct phylink_mac_ops ax88772_phylink_mac_ops = {
    .validate = phylink_generic_validate,
    .mac_config = ax88772_mac_config,
    .mac_link_down = ax88772_mac_link_down,
    .mac_link_up = ax88772_mac_link_up,
};

static int ax88772_phylink_setup(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;
    phy_interface_t phy_if_mode;
    struct phylink *phylink;

    priv->phylink_config.dev = &dev->net->dev;
    priv->phylink_config.type = PHYLINK_NETDEV;
    priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
        MAC_10 | MAC_100;

    __set_bit(PHY_INTERFACE_MODE_INTERNAL,
          priv->phylink_config.supported_interfaces);
    __set_bit(PHY_INTERFACE_MODE_RMII,
          priv->phylink_config.supported_interfaces);

    if (priv->embd_phy)
        phy_if_mode = PHY_INTERFACE_MODE_INTERNAL;
    else
        phy_if_mode = PHY_INTERFACE_MODE_RMII;

    phylink = phylink_create(&priv->phylink_config, dev->net->dev.fwnode,
                 phy_if_mode, &ax88772_phylink_mac_ops);
    if (IS_ERR(phylink))
        return PTR_ERR(phylink);

    priv->phylink = phylink;
    return 0;
}

static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
{
    struct asix_common_private *priv;
    u8 buf[ETH_ALEN] = {0};
    int ret, i;

    priv = devm_kzalloc(&dev->udev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    dev->driver_priv = priv;

    usbnet_get_endpoints(dev, intf);

    /* Maybe the boot loader passed the MAC address via device tree */
    if (!eth_platform_get_mac_address(&dev->udev->dev, buf)) {
        netif_dbg(dev, ifup, dev->net,
              "MAC address read from device tree");
    } else {
        /* Try getting the MAC address from EEPROM */
        if (dev->driver_info->data & FLAG_EEPROM_MAC) {
            for (i = 0; i < (ETH_ALEN >> 1); i++) {
                ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM,
                            0x04 + i, 0, 2, buf + i * 2,
                            0);
                if (ret < 0)
                    break;
            }
        } else {
            ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
                        0, 0, ETH_ALEN, buf, 0);
        }

        if (ret < 0) {
            netdev_dbg(dev->net, "Failed to read MAC address: %d\n",
                   ret);
            return ret;
        }
    }

    asix_set_netdev_dev_addr(dev, buf);

    dev->net->netdev_ops = &ax88772_netdev_ops;
    dev->net->ethtool_ops = &ax88772_ethtool_ops;
    dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
    dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */

    ret = asix_read_phy_addr(dev, true);
    if (ret < 0)
        return ret;

    priv->phy_addr = ret;
    priv->embd_phy = ((priv->phy_addr & 0x1f) == AX_EMBD_PHY_ADDR);

    ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1,
                &priv->chipcode, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read STATMNGSTS_REG: %d\n", ret);
        return ret;
    }

    priv->chipcode &= AX_CHIPCODE_MASK;

    priv->resume = ax88772_resume;
    priv->suspend = ax88772_suspend;
    if (priv->chipcode == AX_AX88772_CHIPCODE)
        priv->reset = ax88772_hw_reset;
    else
        priv->reset = ax88772a_hw_reset;

    ret = priv->reset(dev, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to reset AX88772: %d\n", ret);
        return ret;
    }

    /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
    if (dev->driver_info->flags & FLAG_FRAMING_AX) {
        /* hard_mtu  is still the default - the device does not support
           jumbo eth frames */
        dev->rx_urb_size = 2048;
    }

    priv->presvd_phy_bmcr = 0;
    priv->presvd_phy_advertise = 0;

    ret = ax88772_init_mdio(dev);
    if (ret)
        return ret;

    ret = ax88772_phylink_setup(dev);
    if (ret)
        return ret;

    ret = ax88772_init_phy(dev);
    if (ret)
        phylink_destroy(priv->phylink);

    return ret;
}

static int ax88772_stop(struct usbnet *dev)
{
    struct asix_common_private *priv = dev->driver_priv;

    phylink_stop(priv->phylink);

    return 0;
}

static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
{
    struct asix_common_private *priv = dev->driver_priv;

    rtnl_lock();
    phylink_disconnect_phy(priv->phylink);
    rtnl_unlock();
    phylink_destroy(priv->phylink);
    asix_rx_fixup_common_free(dev->driver_priv);
}

static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf)
{
    asix_rx_fixup_common_free(dev->driver_priv);
    kfree(dev->driver_priv);
}

static const struct ethtool_ops ax88178_ethtool_ops = {
    .get_drvinfo        = asix_get_drvinfo,
    .get_link        = asix_get_link,
    .get_msglevel        = usbnet_get_msglevel,
    .set_msglevel        = usbnet_set_msglevel,
    .get_wol        = asix_get_wol,
    .set_wol        = asix_set_wol,
    .get_eeprom_len        = asix_get_eeprom_len,
    .get_eeprom        = asix_get_eeprom,
    .set_eeprom        = asix_set_eeprom,
    .nway_reset        = usbnet_nway_reset,
    .get_link_ksettings    = usbnet_get_link_ksettings_mii,
    .set_link_ksettings    = usbnet_set_link_ksettings_mii,
};

static int marvell_phy_init(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    u16 reg;

    netdev_dbg(dev->net, "marvell_phy_init()\n");

    reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS);
    netdev_dbg(dev->net, "MII_MARVELL_STATUS = 0x%04x\n", reg);

    asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL,
            MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY);

    if (data->ledmode) {
        reg = asix_mdio_read(dev->net, dev->mii.phy_id,
            MII_MARVELL_LED_CTRL);
        netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (1) = 0x%04x\n", reg);

        reg &= 0xf8ff;
        reg |= (1 + 0x0100);
        asix_mdio_write(dev->net, dev->mii.phy_id,
            MII_MARVELL_LED_CTRL, reg);

        reg = asix_mdio_read(dev->net, dev->mii.phy_id,
            MII_MARVELL_LED_CTRL);
        netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg);
    }

    return 0;
}

static int rtl8211cl_phy_init(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;

    netdev_dbg(dev->net, "rtl8211cl_phy_init()\n");

    asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0005);
    asix_mdio_write (dev->net, dev->mii.phy_id, 0x0c, 0);
    asix_mdio_write (dev->net, dev->mii.phy_id, 0x01,
        asix_mdio_read (dev->net, dev->mii.phy_id, 0x01) | 0x0080);
    asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);

    if (data->ledmode == 12) {
        asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0002);
        asix_mdio_write (dev->net, dev->mii.phy_id, 0x1a, 0x00cb);
        asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0);
    }

    return 0;
}

static int marvell_led_status(struct usbnet *dev, u16 speed)
{
    u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);

    netdev_dbg(dev->net, "marvell_led_status() read 0x%04x\n", reg);

    /* Clear out the center LED bits - 0x03F0 */
    reg &= 0xfc0f;

    switch (speed) {
        case SPEED_1000:
            reg |= 0x03e0;
            break;
        case SPEED_100:
            reg |= 0x03b0;
            break;
        default:
            reg |= 0x02f0;
    }

    netdev_dbg(dev->net, "marvell_led_status() writing 0x%04x\n", reg);
    asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg);

    return 0;
}

static int ax88178_reset(struct usbnet *dev)
{
    struct asix_data *data = (struct asix_data *)&dev->data;
    int ret;
    __le16 eeprom;
    u8 status;
    int gpio0 = 0;
    u32 phyid;

    ret = asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read GPIOS: %d\n", ret);
        return ret;
    }

    netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status);

    asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0);
    ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read EEPROM: %d\n", ret);
        return ret;
    }

    asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0);

    netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom);

    if (eeprom == cpu_to_le16(0xffff)) {
        data->phymode = PHY_MODE_MARVELL;
        data->ledmode = 0;
        gpio0 = 1;
    } else {
        data->phymode = le16_to_cpu(eeprom) & 0x7F;
        data->ledmode = le16_to_cpu(eeprom) >> 8;
        gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1;
    }
    netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode);

    /* Power up external GigaPHY through AX88178 GPIO pin */
    asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 |
            AX_GPIO_GPO1EN, 40, 0);
    if ((le16_to_cpu(eeprom) >> 8) != 1) {
        asix_write_gpio(dev, 0x003c, 30, 0);
        asix_write_gpio(dev, 0x001c, 300, 0);
        asix_write_gpio(dev, 0x003c, 30, 0);
    } else {
        netdev_dbg(dev->net, "gpio phymode == 1 path\n");
        asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0);
        asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0);
    }

    /* Read PHYID register *AFTER* powering up PHY */
    phyid = asix_get_phyid(dev);
    netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);

    /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
    asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0);

    asix_sw_reset(dev, 0, 0);
    msleep(150);

    asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
    msleep(150);

    asix_write_rx_ctl(dev, 0, 0);

    if (data->phymode == PHY_MODE_MARVELL) {
        marvell_phy_init(dev);
        msleep(60);
    } else if (data->phymode == PHY_MODE_RTL8211CL)
        rtl8211cl_phy_init(dev);

    asix_phy_reset(dev, BMCR_RESET | BMCR_ANENABLE);
    asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
            ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
    asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
            ADVERTISE_1000FULL);

    asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0);
    mii_nway_restart(&dev->mii);

    /* Rewrite MAC address */
    memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
    ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
                            data->mac_addr, 0);
    if (ret < 0)
        return ret;

    ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
    if (ret < 0)
        return ret;

    return 0;
}

static int ax88178_link_reset(struct usbnet *dev)
{
    u16 mode;
    struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
    struct asix_data *data = (struct asix_data *)&dev->data;
    u32 speed;

    netdev_dbg(dev->net, "ax88178_link_reset()\n");

    mii_check_media(&dev->mii, 1, 1);
    mii_ethtool_gset(&dev->mii, &ecmd);
    mode = AX88178_MEDIUM_DEFAULT;
    speed = ethtool_cmd_speed(&ecmd);

    if (speed == SPEED_1000)
        mode |= AX_MEDIUM_GM;
    else if (speed == SPEED_100)
        mode |= AX_MEDIUM_PS;
    else
        mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM);

    mode |= AX_MEDIUM_ENCK;

    if (ecmd.duplex == DUPLEX_FULL)
        mode |= AX_MEDIUM_FD;
    else
        mode &= ~AX_MEDIUM_FD;

    netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
           speed, ecmd.duplex, mode);

    asix_write_medium_mode(dev, mode, 0);

    if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
        marvell_led_status(dev, speed);

    return 0;
}

static void ax88178_set_mfb(struct usbnet *dev)
{
    u16 mfb = AX_RX_CTL_MFB_16384;
    u16 rxctl;
    u16 medium;
    int old_rx_urb_size = dev->rx_urb_size;

    if (dev->hard_mtu < 2048) {
        dev->rx_urb_size = 2048;
        mfb = AX_RX_CTL_MFB_2048;
    } else if (dev->hard_mtu < 4096) {
        dev->rx_urb_size = 4096;
        mfb = AX_RX_CTL_MFB_4096;
    } else if (dev->hard_mtu < 8192) {
        dev->rx_urb_size = 8192;
        mfb = AX_RX_CTL_MFB_8192;
    } else if (dev->hard_mtu < 16384) {
        dev->rx_urb_size = 16384;
        mfb = AX_RX_CTL_MFB_16384;
    }

    rxctl = asix_read_rx_ctl(dev, 0);
    asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0);

    medium = asix_read_medium_status(dev, 0);
    if (dev->net->mtu > 1500)
        medium |= AX_MEDIUM_JFE;
    else
        medium &= ~AX_MEDIUM_JFE;
    asix_write_medium_mode(dev, medium, 0);

    if (dev->rx_urb_size > old_rx_urb_size)
        usbnet_unlink_rx_urbs(dev);
}

static int ax88178_change_mtu(struct net_device *net, int new_mtu)
{
    struct usbnet *dev = netdev_priv(net);
    int ll_mtu = new_mtu + net->hard_header_len + 4;

    netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu);

    if ((ll_mtu % dev->maxpacket) == 0)
        return -EDOM;

    net->mtu = new_mtu;
    dev->hard_mtu = net->mtu + net->hard_header_len;
    ax88178_set_mfb(dev);

    /* max qlen depend on hard_mtu and rx_urb_size */
    usbnet_update_max_qlen(dev);

    return 0;
}

static const struct net_device_ops ax88178_netdev_ops = {
    .ndo_open        = usbnet_open,
    .ndo_stop        = usbnet_stop,
    .ndo_start_xmit        = usbnet_start_xmit,
    .ndo_tx_timeout        = usbnet_tx_timeout,
    .ndo_get_stats64    = dev_get_tstats64,
    .ndo_set_mac_address     = asix_set_mac_address,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_set_rx_mode    = asix_set_multicast,
    .ndo_eth_ioctl        = asix_ioctl,
    .ndo_change_mtu     = ax88178_change_mtu,
};

static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
{
    int ret;
    u8 buf[ETH_ALEN] = {0};

    usbnet_get_endpoints(dev,intf);

    /* Get the MAC address */
    ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
    if (ret < 0) {
        netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret);
        return ret;
    }

    asix_set_netdev_dev_addr(dev, buf);

    /* Initialize MII structure */
    dev->mii.dev = dev->net;
    dev->mii.mdio_read = asix_mdio_read;
    dev->mii.mdio_write = asix_mdio_write;
    dev->mii.phy_id_mask = 0x1f;
    dev->mii.reg_num_mask = 0xff;
    dev->mii.supports_gmii = 1;

    dev->mii.phy_id = asix_read_phy_addr(dev, true);
    if (dev->mii.phy_id < 0)
        return dev->mii.phy_id;

    dev->net->netdev_ops = &ax88178_netdev_ops;
    dev->net->ethtool_ops = &ax88178_ethtool_ops;
    dev->net->max_mtu = 16384 - (dev->net->hard_header_len + 4);

    /* Blink LEDS so users know driver saw dongle */
    asix_sw_reset(dev, 0, 0);
    msleep(150);

    asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
    msleep(150);

    /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
    if (dev->driver_info->flags & FLAG_FRAMING_AX) {
        /* hard_mtu  is still the default - the device does not support
           jumbo eth frames */
        dev->rx_urb_size = 2048;
    }

    dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
    if (!dev->driver_priv)
            return -ENOMEM;

    return 0;
}

static const struct driver_info ax8817x_info = {
    .description = "ASIX AX8817x USB 2.0 Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x00130103,
};

static const struct driver_info dlink_dub_e100_info = {
    .description = "DLink DUB-E100 USB Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x009f9d9f,
};

static const struct driver_info netgear_fa120_info = {
    .description = "Netgear FA-120 USB Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x00130103,
};

static const struct driver_info hawking_uf200_info = {
    .description = "Hawking UF200 USB Ethernet",
    .bind = ax88172_bind,
    .status = asix_status,
    .link_reset = ax88172_link_reset,
    .reset = ax88172_link_reset,
    .flags =  FLAG_ETHER | FLAG_LINK_INTR,
    .data = 0x001f1d1f,
};

static const struct driver_info ax88772_info = {
    .description = "ASIX AX88772 USB 2.0 Ethernet",
    .bind = ax88772_bind,
    .unbind = ax88772_unbind,
    .status = asix_status,
    .reset = ax88772_reset,
    .stop = ax88772_stop,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
};

static const struct driver_info ax88772b_info = {
    .description = "ASIX AX88772B USB 2.0 Ethernet",
    .bind = ax88772_bind,
    .unbind = ax88772_unbind,
    .status = asix_status,
    .reset = ax88772_reset,
    .stop = ax88772_stop,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
             FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
    .data = FLAG_EEPROM_MAC,
};

static const struct driver_info ax88178_info = {
    .description = "ASIX AX88178 USB 2.0 Ethernet",
    .bind = ax88178_bind,
    .unbind = ax88178_unbind,
    .status = asix_status,
    .link_reset = ax88178_link_reset,
    .reset = ax88178_reset,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
         FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
};

/*
 * USBLINK 20F9 "USB 2.0 LAN" USB ethernet adapter, typically found in
 * no-name packaging.
 * USB device strings are:
 *   1: Manufacturer: USBLINK
 *   2: Product: HG20F9 USB2.0
 *   3: Serial: 000003
 * Appears to be compatible with Asix 88772B.
 */
static const struct driver_info hg20f9_info = {
    .description = "HG20F9 USB 2.0 Ethernet",
    .bind = ax88772_bind,
    .unbind = ax88772_unbind,
    .status = asix_status,
    .reset = ax88772_reset,
    .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
             FLAG_MULTI_PACKET,
    .rx_fixup = asix_rx_fixup_common,
    .tx_fixup = asix_tx_fixup,
    .data = FLAG_EEPROM_MAC,
};

static const struct usb_device_id    products [] = {
{
    // Linksys USB200M
    USB_DEVICE (0x077b, 0x2226),
    .driver_info =    (unsigned long) &ax8817x_info,
}, {
    // Netgear FA120
    USB_DEVICE (0x0846, 0x1040),
    .driver_info =  (unsigned long) &netgear_fa120_info,
}, {
    // DLink DUB-E100
    USB_DEVICE (0x2001, 0x1a00),
    .driver_info =  (unsigned long) &dlink_dub_e100_info,
}, {
    // Intellinet, ST Lab USB Ethernet
    USB_DEVICE (0x0b95, 0x1720),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Hawking UF200, TrendNet TU2-ET100
    USB_DEVICE (0x07b8, 0x420a),
    .driver_info =  (unsigned long) &hawking_uf200_info,
}, {
    // Billionton Systems, USB2AR
    USB_DEVICE (0x08dd, 0x90ff),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Billionton Systems, GUSB2AM-1G-B
    USB_DEVICE(0x08dd, 0x0114),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // ATEN UC210T
    USB_DEVICE (0x0557, 0x2009),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Buffalo LUA-U2-KTX
    USB_DEVICE (0x0411, 0x003d),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Buffalo LUA-U2-GT 10/100/1000
    USB_DEVICE (0x0411, 0x006e),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
    USB_DEVICE (0x6189, 0x182d),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter"
    USB_DEVICE (0x0df6, 0x0056),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter"
    USB_DEVICE (0x0df6, 0x061c),
    .driver_info =  (unsigned long) &ax88178_info,
}, {
    // corega FEther USB2-TX
    USB_DEVICE (0x07aa, 0x0017),
    .driver_info =  (unsigned long) &ax8817x_info,
}, {
    // Surecom EP-1427X-2
    USB_DEVICE (0x1189, 0x0893),
    .driver_info = (unsigned long) &ax8817x_info,
}, {
    // goodway corp usb gwusb2e
    USB_DEVICE (0x1631, 0x6200),
    .driver_info = (unsigned long) &ax8817x_info,
}, {
    // JVC MP-PRX1 Port Replicator
    USB_DEVICE (0x04f1, 0x3008),
    .driver_info = (unsigned long) &ax8817x_info,
}, {
    // Lenovo U2L100P 10/100
    USB_DEVICE (0x17ef, 0x7203),
    .driver_info = (unsigned long)&ax88772b_info,
}, {
    // ASIX AX88772B 10/100
    USB_DEVICE (0x0b95, 0x772b),
    .driver_info = (unsigned long) &ax88772b_info,
}, {
    // ASIX AX88772 10/100
    USB_DEVICE (0x0b95, 0x7720),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // ASIX AX88178 10/100/1000
    USB_DEVICE (0x0b95, 0x1780),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Logitec LAN-GTJ/U2A
    USB_DEVICE (0x0789, 0x0160),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Linksys USB200M Rev 2
    USB_DEVICE (0x13b1, 0x0018),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // 0Q0 cable ethernet
    USB_DEVICE (0x1557, 0x7720),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // DLink DUB-E100 H/W Ver B1
    USB_DEVICE (0x07d1, 0x3c05),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // DLink DUB-E100 H/W Ver B1 Alternate
    USB_DEVICE (0x2001, 0x3c05),
    .driver_info = (unsigned long) &ax88772_info,
}, {
       // DLink DUB-E100 H/W Ver C1
       USB_DEVICE (0x2001, 0x1a02),
       .driver_info = (unsigned long) &ax88772_info,
}, {
    // Linksys USB1000
    USB_DEVICE (0x1737, 0x0039),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // IO-DATA ETG-US2
    USB_DEVICE (0x04bb, 0x0930),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Belkin F5D5055
    USB_DEVICE(0x050d, 0x5055),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // Apple USB Ethernet Adapter
    USB_DEVICE(0x05ac, 0x1402),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // Cables-to-Go USB Ethernet Adapter
    USB_DEVICE(0x0b95, 0x772a),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // ABOCOM for pci
    USB_DEVICE(0x14ea, 0xab11),
    .driver_info = (unsigned long) &ax88178_info,
}, {
    // ASIX 88772a
    USB_DEVICE(0x0db0, 0xa877),
    .driver_info = (unsigned long) &ax88772_info,
}, {
    // Asus USB Ethernet Adapter
    USB_DEVICE (0x0b95, 0x7e2b),
    .driver_info = (unsigned long)&ax88772b_info,
}, {
    /* ASIX 88172a demo board */
    USB_DEVICE(0x0b95, 0x172a),
    .driver_info = (unsigned long) &ax88172a_info,
}, {
    /*
     * USBLINK HG20F9 "USB 2.0 LAN"
     * Appears to have gazumped Linksys's manufacturer ID but
     * doesn't (yet) conflict with any known Linksys product.
     */
    USB_DEVICE(0x066b, 0x20f9),
    .driver_info = (unsigned long) &hg20f9_info,
},
    { },        // END
};
MODULE_DEVICE_TABLE(usb, products);

static struct usb_driver asix_driver = {
    .name =        DRIVER_NAME,
    .id_table =    products,
    .probe =    usbnet_probe,
    .suspend =    asix_suspend,
    .resume =    asix_resume,
    .reset_resume =    asix_resume,
    .disconnect =    usbnet_disconnect,
    .supports_autosuspend = 1,
    .disable_hub_initiated_lpm = 1,
};

module_usb_driver(asix_driver);

MODULE_AUTHOR("David Hollis");
MODULE_VERSION(DRIVER_VERSION);
MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices");
MODULE_LICENSE("GPL");

Usb ethernet driver?
lol
 
  • Like
Reactions: Hmed

Hmed

Well-Known Member
Newcomer
Joined
Sep 20, 2017
Messages
53
Trophies
0
Age
42
Location
Blida
XP
1,431
Country
Algeria
Hi good luck everyone thank you gbatemp
Post automatically merged:

Happy Halloween for all the scene and happy birthday gbatemp
 
Status
Not open for further replies.

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    Metoroid0 @ Metoroid0: im more interested in metroid prime 4