diff options
author | Robert Foss <robert.foss@collabora.com> | 2016-08-29 09:32:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-08-31 21:07:05 -0700 |
commit | d9fe64e511144c1ee7d7555b4111f09dde9692ef (patch) | |
tree | 2e69c80dc328131d744ae10b494c227c0a659079 /drivers/net/usb | |
parent | c7735f1bac209e285839ccead00873b27afc013b (diff) |
net: asix: Add in_pm parameter
From: Freddy Xin <freddy@asix.com.tw>
In order to R/W registers in suspend/resume functions, in_pm flags are
added to some functions to determine whether the nopm version of usb
functions is called.
Save BMCR and ANAR PHY registers in suspend function and restore them
in resume function.
Reset HW in resume function to ensure the PHY works correctly.
Signed-off-by: Freddy Xin <freddy@asix.com.tw>
Signed-off-by: Robert Foss <robert.foss@collabora.com>
Tested-by: Robert Foss <robert.foss@collabora.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/asix.h | 40 | ||||
-rw-r--r-- | drivers/net/usb/asix_common.c | 180 | ||||
-rw-r--r-- | drivers/net/usb/asix_devices.c | 373 | ||||
-rw-r--r-- | drivers/net/usb/ax88172a.c | 29 |
4 files changed, 472 insertions, 150 deletions
diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index a2d3ea6efb20..d1092421aaa7 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -46,6 +46,7 @@ #define AX_CMD_SET_SW_MII 0x06 #define AX_CMD_READ_MII_REG 0x07 #define AX_CMD_WRITE_MII_REG 0x08 +#define AX_CMD_STATMNGSTS_REG 0x09 #define AX_CMD_SET_HW_MII 0x0a #define AX_CMD_READ_EEPROM 0x0b #define AX_CMD_WRITE_EEPROM 0x0c @@ -71,6 +72,17 @@ #define AX_CMD_SW_RESET 0x20 #define AX_CMD_SW_PHY_STATUS 0x21 #define AX_CMD_SW_PHY_SELECT 0x22 +#define AX_QCTCTRL 0x2A + +#define AX_CHIPCODE_MASK 0x70 +#define AX_AX88772_CHIPCODE 0x00 +#define AX_AX88772A_CHIPCODE 0x10 +#define AX_AX88772B_CHIPCODE 0x20 +#define AX_HOST_EN 0x01 + +#define AX_PHYSEL_PSEL 0x01 +#define AX_PHYSEL_SSMII 0 +#define AX_PHYSEL_SSEN 0x10 #define AX_PHY_SELECT_MASK (BIT(3) | BIT(2)) #define AX_PHY_SELECT_INTERNAL 0 @@ -173,6 +185,10 @@ struct asix_rx_fixup_info { }; struct asix_common_private { + void (*resume)(struct usbnet *dev); + void (*suspend)(struct usbnet *dev); + u16 presvd_phy_advertise; + u16 presvd_phy_bmcr; struct asix_rx_fixup_info rx_fixup_info; }; @@ -182,10 +198,10 @@ extern const struct driver_info ax88172a_info; #define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */ int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data); + u16 size, void *data, int in_pm); int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data); + u16 size, void *data, int in_pm); void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data); @@ -197,27 +213,31 @@ int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb); struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); -int asix_set_sw_mii(struct usbnet *dev); -int asix_set_hw_mii(struct usbnet *dev); +int asix_set_sw_mii(struct usbnet *dev, int in_pm); +int asix_set_hw_mii(struct usbnet *dev, int in_pm); int asix_read_phy_addr(struct usbnet *dev, int internal); int asix_get_phy_addr(struct usbnet *dev); -int asix_sw_reset(struct usbnet *dev, u8 flags); +int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm); -u16 asix_read_rx_ctl(struct usbnet *dev); -int asix_write_rx_ctl(struct usbnet *dev, u16 mode); +u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm); +int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm); -u16 asix_read_medium_status(struct usbnet *dev); -int asix_write_medium_mode(struct usbnet *dev, u16 mode); +u16 asix_read_medium_status(struct usbnet *dev, int in_pm); +int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm); -int asix_write_gpio(struct usbnet *dev, u16 value, int sleep); +int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm); void asix_set_multicast(struct net_device *net); int asix_mdio_read(struct net_device *netdev, int phy_id, int loc); void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val); +int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc); +void asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, + int val); + void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo); int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo); diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 7de5ab589e4e..25609eefc762 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -22,24 +22,49 @@ #include "asix.h" int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data) + u16 size, void *data, int in_pm) { int ret; - ret = usbnet_read_cmd(dev, cmd, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, data, size); + int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); + + BUG_ON(!dev); + + if (!in_pm) + fn = usbnet_read_cmd; + else + fn = usbnet_read_cmd_nopm; + + ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, size); + + if (unlikely(ret < 0)) + netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", + index, ret); - if (ret != size && ret >= 0) - return -EINVAL; return ret; } int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, - u16 size, void *data) + u16 size, void *data, int in_pm) { - return usbnet_write_cmd(dev, cmd, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, data, size); + int ret; + int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); + + BUG_ON(!dev); + + if (!in_pm) + fn = usbnet_write_cmd; + else + fn = usbnet_write_cmd_nopm; + + ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, size); + + if (unlikely(ret < 0)) + netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", + index, ret); + + return ret; } void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, @@ -225,19 +250,20 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, return skb; } -int asix_set_sw_mii(struct usbnet *dev) +int asix_set_sw_mii(struct usbnet *dev, int in_pm) { int ret; - ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm); + if (ret < 0) netdev_err(dev->net, "Failed to enable software MII access\n"); return ret; } -int asix_set_hw_mii(struct usbnet *dev) +int asix_set_hw_mii(struct usbnet *dev, int in_pm) { int ret; - ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm); if (ret < 0) netdev_err(dev->net, "Failed to enable hardware MII access\n"); return ret; @@ -247,7 +273,7 @@ int asix_read_phy_addr(struct usbnet *dev, int internal) { int offset = (internal ? 1 : 0); u8 buf[2]; - int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); + int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0); netdev_dbg(dev->net, "asix_get_phy_addr()\n"); @@ -270,21 +296,21 @@ int asix_get_phy_addr(struct usbnet *dev) } -int asix_sw_reset(struct usbnet *dev, u8 flags) +int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm) { int ret; - ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm); if (ret < 0) netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); return ret; } -u16 asix_read_rx_ctl(struct usbnet *dev) +u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm) { __le16 v; - int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v); + int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm); if (ret < 0) { netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); @@ -295,12 +321,12 @@ out: return ret; } -int asix_write_rx_ctl(struct usbnet *dev, u16 mode) +int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm) { int ret; netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); - ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm); if (ret < 0) netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", mode, ret); @@ -308,10 +334,11 @@ int asix_write_rx_ctl(struct usbnet *dev, u16 mode) return ret; } -u16 asix_read_medium_status(struct usbnet *dev) +u16 asix_read_medium_status(struct usbnet *dev, int in_pm) { __le16 v; - int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); + int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, + 0, 0, 2, &v, in_pm); if (ret < 0) { netdev_err(dev->net, "Error reading Medium Status register: %02x\n", @@ -323,12 +350,13 @@ u16 asix_read_medium_status(struct usbnet *dev) } -int asix_write_medium_mode(struct usbnet *dev, u16 mode) +int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm) { int ret; netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); - ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, + mode, 0, 0, NULL, in_pm); if (ret < 0) netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", mode, ret); @@ -336,12 +364,12 @@ int asix_write_medium_mode(struct usbnet *dev, u16 mode) return ret; } -int asix_write_gpio(struct usbnet *dev, u16 value, int sleep) +int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm) { int ret; netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); - ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm); if (ret < 0) netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", value, ret); @@ -398,16 +426,23 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) { struct usbnet *dev = netdev_priv(netdev); __le16 res; + u8 smsr; + int i = 0; mutex_lock(&dev->phy_mutex); - asix_set_sw_mii(dev); + do { + asix_set_sw_mii(dev, 0); + usleep_range(1000, 1100); + asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0); + } while (!(smsr & AX_HOST_EN) && (i++ < 30)); + asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, - (__u16)loc, 2, &res); - asix_set_hw_mii(dev); + (__u16)loc, 2, &res, 0); + asix_set_hw_mii(dev, 0); mutex_unlock(&dev->phy_mutex); netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", - phy_id, loc, le16_to_cpu(res)); + phy_id, loc, le16_to_cpu(res)); return le16_to_cpu(res); } @@ -416,13 +451,71 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) { struct usbnet *dev = netdev_priv(netdev); __le16 res = cpu_to_le16(val); + u8 smsr; + int i = 0; netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", - phy_id, loc, val); + phy_id, loc, val); + + mutex_lock(&dev->phy_mutex); + do { + asix_set_sw_mii(dev, 0); + usleep_range(1000, 1100); + asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0); + } while (!(smsr & AX_HOST_EN) && (i++ < 30)); + + asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, + (__u16)loc, 2, &res, 0); + asix_set_hw_mii(dev, 0); + mutex_unlock(&dev->phy_mutex); +} + +int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc) +{ + struct usbnet *dev = netdev_priv(netdev); + __le16 res; + u8 smsr; + int i = 0; + + mutex_lock(&dev->phy_mutex); + do { + asix_set_sw_mii(dev, 1); + usleep_range(1000, 1100); + asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1); + } while (!(smsr & AX_HOST_EN) && (i++ < 30)); + + asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, + (__u16)loc, 2, &res, 1); + asix_set_hw_mii(dev, 1); + mutex_unlock(&dev->phy_mutex); + + netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", + phy_id, loc, le16_to_cpu(res)); + + return le16_to_cpu(res); +} + +void +asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val) +{ + struct usbnet *dev = netdev_priv(netdev); + __le16 res = cpu_to_le16(val); + u8 smsr; + int i = 0; + + netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", + phy_id, loc, val); + mutex_lock(&dev->phy_mutex); - asix_set_sw_mii(dev); - asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); - asix_set_hw_mii(dev); + do { + asix_set_sw_mii(dev, 1); + usleep_range(1000, 1100); + asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1); + } while (!(smsr & AX_HOST_EN) && (i++ < 30)); + + asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, + (__u16)loc, 2, &res, 1); + asix_set_hw_mii(dev, 1); mutex_unlock(&dev->phy_mutex); } @@ -431,7 +524,8 @@ void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) struct usbnet *dev = netdev_priv(net); u8 opt; - if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { + if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, + 0, 0, 1, &opt, 0) < 0) { wolinfo->supported = 0; wolinfo->wolopts = 0; return; @@ -455,7 +549,7 @@ int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) opt |= AX_MONITOR_MAGIC; if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, - opt, 0, 0, NULL) < 0) + opt, 0, 0, NULL, 0) < 0) return -EINVAL; return 0; @@ -490,7 +584,7 @@ int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, /* ax8817x returns 2 bytes from eeprom on read */ for (i = first_word; i <= last_word; i++) { if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, - &(eeprom_buff[i - first_word])) < 0) { + &eeprom_buff[i - first_word], 0) < 0) { kfree(eeprom_buff); return -EIO; } @@ -531,7 +625,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, the EEPROM */ if (eeprom->offset & 1) { ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, - &(eeprom_buff[0])); + &eeprom_buff[0], 0); if (ret < 0) { netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); goto free; @@ -540,7 +634,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, if ((eeprom->offset + eeprom->len) & 1) { ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, - &(eeprom_buff[last_word - first_word])); + &eeprom_buff[last_word - first_word], 0); if (ret < 0) { netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); goto free; @@ -550,7 +644,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); /* write data to EEPROM */ - ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0); if (ret < 0) { netdev_err(net, "Failed to enable EEPROM write\n"); goto free; @@ -561,7 +655,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", i, eeprom_buff[i - first_word]); ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, - eeprom_buff[i - first_word], 0, NULL); + eeprom_buff[i - first_word], 0, NULL, 0); if (ret < 0) { netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i); @@ -570,7 +664,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, msleep(20); } - ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0); if (ret < 0) { netdev_err(net, "Failed to disable EEPROM write\n"); goto free; diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 5cabefc23494..aaa42909d932 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -184,7 +184,7 @@ static int ax88172_link_reset(struct usbnet *dev) 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); + asix_write_medium_mode(dev, mode, 0); return 0; } @@ -213,18 +213,19 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *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); + (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0); if (ret < 0) goto out; msleep(5); } - ret = asix_write_rx_ctl(dev, 0x80); + 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); + 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); @@ -290,7 +291,7 @@ static int ax88772_link_reset(struct usbnet *dev) netdev_dbg(dev->net, "ax88772_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); + asix_write_medium_mode(dev, mode, 0); return 0; } @@ -298,78 +299,192 @@ static int ax88772_link_reset(struct usbnet *dev) static int ax88772_reset(struct usbnet *dev) { struct asix_data *data = (struct asix_data *)&dev->data; + 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; + + asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0); + if (ret < 0) + goto out; + + 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; int ret, embd_phy; u16 rx_ctl; - ret = asix_write_gpio(dev, - AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5); + ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 | + AX_GPIO_GPO2EN, 5, in_pm); if (ret < 0) goto out; - embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); + embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); - ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); + ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, + 0, 0, NULL, in_pm); if (ret < 0) { netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); goto out; } - ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL); - if (ret < 0) - goto out; + if (embd_phy) { + ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm); + if (ret < 0) + goto out; - msleep(150); + usleep_range(10000, 11000); - ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); - if (ret < 0) - goto out; + ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm); + if (ret < 0) + goto out; - msleep(150); + msleep(60); - if (embd_phy) { - ret = asix_sw_reset(dev, AX_SWRESET_IPRL); + 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_PRTE); + ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL, + in_pm); if (ret < 0) goto out; } msleep(150); - rx_ctl = asix_read_rx_ctl(dev); - netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); - ret = asix_write_rx_ctl(dev, 0x0000); + + 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; - rx_ctl = asix_read_rx_ctl(dev); - netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); + 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; + } - ret = asix_sw_reset(dev, AX_SWRESET_PRL); + /* 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; - msleep(150); + /* 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; + int ret, embd_phy; + u16 rx_ctl; + u8 chipcode = 0; - ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL); + ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm); if (ret < 0) goto out; - msleep(150); + embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); - asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); - asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_CSMA); - mii_nway_restart(&dev->mii); + ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 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_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT); + 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; + } + + ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, + 0, 1, &chipcode, in_pm); + if (ret < 0) + goto out; + + if ((chipcode & AX_CHIPCODE_MASK) == 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; + } + } + ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, - AX88772_IPG2_DEFAULT, 0, NULL); + AX88772_IPG2_DEFAULT, 0, NULL, in_pm); if (ret < 0) { netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); goto out; @@ -378,20 +493,29 @@ static int ax88772_reset(struct usbnet *dev) /* 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); + 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); + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm); if (ret < 0) goto out; - rx_ctl = asix_read_rx_ctl(dev); + 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); + rx_ctl = asix_read_medium_status(dev, in_pm); netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n", rx_ctl); @@ -400,7 +524,6 @@ static int ax88772_reset(struct usbnet *dev) out: return ret; - } static const struct net_device_ops ax88772_netdev_ops = { @@ -415,11 +538,87 @@ static const struct net_device_ops ax88772_netdev_ops = { .ndo_set_rx_mode = asix_set_multicast, }; +static void ax88772_suspend(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + + /* Preserve BMCR for restoring */ + priv->presvd_phy_bmcr = + asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_BMCR); + + /* Preserve ANAR for restoring */ + priv->presvd_phy_advertise = + asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE); +} + +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->suspend) + priv->suspend(dev); + + return usbnet_suspend(intf, message); +} + +static void ax88772_restore_phy(struct usbnet *dev) +{ + struct asix_common_private *priv = dev->driver_priv; + + if (priv->presvd_phy_advertise) { + /* Restore Advertisement control reg */ + asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE, + priv->presvd_phy_advertise); + + /* Restore BMCR */ + asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_BMCR, + priv->presvd_phy_bmcr); + + priv->presvd_phy_advertise = 0; + priv->presvd_phy_bmcr = 0; + } +} + +static void ax88772_resume(struct usbnet *dev) +{ + int i; + + for (i = 0; i < 3; i++) + if (!ax88772_hw_reset(dev, 1)) + break; + ax88772_restore_phy(dev); +} + +static void ax88772a_resume(struct usbnet *dev) +{ + int i; + + for (i = 0; i < 3; i++) { + if (!ax88772a_hw_reset(dev, 1)) + break; + } + + ax88772_restore_phy(dev); +} + +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->resume) + priv->resume(dev); + + return usbnet_resume(intf); +} + static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) { - int ret, embd_phy, i; - u8 buf[ETH_ALEN]; + int ret, i; + u8 buf[ETH_ALEN], chipcode = 0; u32 phyid; + struct asix_common_private *priv; usbnet_get_endpoints(dev,intf); @@ -427,13 +626,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) 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, 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, 0, ETH_ALEN, buf, 0); } if (ret < 0) { @@ -456,16 +655,11 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ - embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); + asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &chipcode, 0); + chipcode &= AX_CHIPCODE_MASK; - /* Reset the PHY to normal operation mode */ - ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); - if (ret < 0) { - netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); - return ret; - } - - ax88772_reset(dev); + (chipcode == AX_AX88772_CHIPCODE) ? ax88772_hw_reset(dev, 0) : + ax88772a_hw_reset(dev, 0); /* Read PHYID register *AFTER* the PHY was reset properly */ phyid = asix_get_phyid(dev); @@ -482,6 +676,18 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) if (!dev->driver_priv) return -ENOMEM; + priv = dev->driver_priv; + + priv->presvd_phy_bmcr = 0; + priv->presvd_phy_advertise = 0; + if (chipcode == AX_AX88772_CHIPCODE) { + priv->resume = ax88772_resume; + priv->suspend = ax88772_suspend; + } else { + priv->resume = ax88772a_resume; + priv->suspend = ax88772_suspend; + } + return 0; } @@ -593,12 +799,12 @@ static int ax88178_reset(struct usbnet *dev) int gpio0 = 0; u32 phyid; - asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status); + asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0); netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status); - asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL); - asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom); - asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL); + asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0); + asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0); + 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); @@ -614,15 +820,16 @@ static int ax88178_reset(struct usbnet *dev) 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); + 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); - asix_write_gpio(dev, 0x001c, 300); - asix_write_gpio(dev, 0x003c, 30); + 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); - asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30); + 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 */ @@ -630,15 +837,15 @@ static int ax88178_reset(struct usbnet *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); + asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0); - asix_sw_reset(dev, 0); + asix_sw_reset(dev, 0, 0); msleep(150); - asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); + asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0); msleep(150); - asix_write_rx_ctl(dev, 0); + asix_write_rx_ctl(dev, 0, 0); if (data->phymode == PHY_MODE_MARVELL) { marvell_phy_init(dev); @@ -655,18 +862,18 @@ static int ax88178_reset(struct usbnet *dev) mii_nway_restart(&dev->mii); - ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT); + ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0); if (ret < 0) return ret; /* 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); + data->mac_addr, 0); if (ret < 0) return ret; - ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); + ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0); if (ret < 0) return ret; @@ -704,7 +911,7 @@ static int ax88178_link_reset(struct usbnet *dev) 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); + asix_write_medium_mode(dev, mode, 0); if (data->phymode == PHY_MODE_MARVELL && data->ledmode) marvell_led_status(dev, speed); @@ -733,15 +940,15 @@ static void ax88178_set_mfb(struct usbnet *dev) mfb = AX_RX_CTL_MFB_16384; } - rxctl = asix_read_rx_ctl(dev); - asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb); + 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); + 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); + asix_write_medium_mode(dev, medium, 0); if (dev->rx_urb_size > old_rx_urb_size) usbnet_unlink_rx_urbs(dev); @@ -790,7 +997,7 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) usbnet_get_endpoints(dev,intf); /* Get the MAC address */ - ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); + 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; @@ -811,10 +1018,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->ethtool_ops = &ax88178_ethtool_ops; /* Blink LEDS so users know driver saw dongle */ - asix_sw_reset(dev, 0); + asix_sw_reset(dev, 0, 0); msleep(150); - asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); + 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 */ @@ -877,7 +1084,7 @@ static const struct driver_info ax88772_info = { .unbind = ax88772_unbind, .status = asix_status, .link_reset = ax88772_link_reset, - .reset = ax |