18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci /*************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007-2010 SMSC 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci *****************************************************************************/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kmod.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 138c2ecf20Sopenharmony_ci#include <linux/mii.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci#include <linux/bitrev.h> 168c2ecf20Sopenharmony_ci#include <linux/crc16.h> 178c2ecf20Sopenharmony_ci#include <linux/crc32.h> 188c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/of_net.h> 218c2ecf20Sopenharmony_ci#include "smsc75xx.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define SMSC_CHIPNAME "smsc75xx" 248c2ecf20Sopenharmony_ci#define SMSC_DRIVER_VERSION "1.0.0" 258c2ecf20Sopenharmony_ci#define HS_USB_PKT_SIZE (512) 268c2ecf20Sopenharmony_ci#define FS_USB_PKT_SIZE (64) 278c2ecf20Sopenharmony_ci#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) 288c2ecf20Sopenharmony_ci#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) 298c2ecf20Sopenharmony_ci#define DEFAULT_BULK_IN_DELAY (0x00002000) 308c2ecf20Sopenharmony_ci#define MAX_SINGLE_PACKET_SIZE (9000) 318c2ecf20Sopenharmony_ci#define LAN75XX_EEPROM_MAGIC (0x7500) 328c2ecf20Sopenharmony_ci#define EEPROM_MAC_OFFSET (0x01) 338c2ecf20Sopenharmony_ci#define DEFAULT_TX_CSUM_ENABLE (true) 348c2ecf20Sopenharmony_ci#define DEFAULT_RX_CSUM_ENABLE (true) 358c2ecf20Sopenharmony_ci#define SMSC75XX_INTERNAL_PHY_ID (1) 368c2ecf20Sopenharmony_ci#define SMSC75XX_TX_OVERHEAD (8) 378c2ecf20Sopenharmony_ci#define MAX_RX_FIFO_SIZE (20 * 1024) 388c2ecf20Sopenharmony_ci#define MAX_TX_FIFO_SIZE (12 * 1024) 398c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_SMSC (0x0424) 408c2ecf20Sopenharmony_ci#define USB_PRODUCT_ID_LAN7500 (0x7500) 418c2ecf20Sopenharmony_ci#define USB_PRODUCT_ID_LAN7505 (0x7505) 428c2ecf20Sopenharmony_ci#define RXW_PADDING 2 438c2ecf20Sopenharmony_ci#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ 448c2ecf20Sopenharmony_ci WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SUSPEND_SUSPEND0 (0x01) 478c2ecf20Sopenharmony_ci#define SUSPEND_SUSPEND1 (0x02) 488c2ecf20Sopenharmony_ci#define SUSPEND_SUSPEND2 (0x04) 498c2ecf20Sopenharmony_ci#define SUSPEND_SUSPEND3 (0x08) 508c2ecf20Sopenharmony_ci#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ 518c2ecf20Sopenharmony_ci SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct smsc75xx_priv { 548c2ecf20Sopenharmony_ci struct usbnet *dev; 558c2ecf20Sopenharmony_ci u32 rfe_ctl; 568c2ecf20Sopenharmony_ci u32 wolopts; 578c2ecf20Sopenharmony_ci u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; 588c2ecf20Sopenharmony_ci struct mutex dataport_mutex; 598c2ecf20Sopenharmony_ci spinlock_t rfe_ctl_lock; 608c2ecf20Sopenharmony_ci struct work_struct set_multicast; 618c2ecf20Sopenharmony_ci u8 suspend_flags; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct usb_context { 658c2ecf20Sopenharmony_ci struct usb_ctrlrequest req; 668c2ecf20Sopenharmony_ci struct usbnet *dev; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic bool turbo_mode = true; 708c2ecf20Sopenharmony_cimodule_param(turbo_mode, bool, 0644); 718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int smsc75xx_link_ok_nopm(struct usbnet *dev); 748c2ecf20Sopenharmony_cistatic int smsc75xx_phy_gig_workaround(struct usbnet *dev); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, 778c2ecf20Sopenharmony_ci u32 *data, int in_pm) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci u32 buf; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci BUG_ON(!dev); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!in_pm) 868c2ecf20Sopenharmony_ci fn = usbnet_read_cmd; 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci fn = usbnet_read_cmd_nopm; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN 918c2ecf20Sopenharmony_ci | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 928c2ecf20Sopenharmony_ci 0, index, &buf, 4); 938c2ecf20Sopenharmony_ci if (unlikely(ret < 4)) { 948c2ecf20Sopenharmony_ci ret = ret < 0 ? ret : -ENODATA; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", 978c2ecf20Sopenharmony_ci index, ret); 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci le32_to_cpus(&buf); 1028c2ecf20Sopenharmony_ci *data = buf; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index, 1088c2ecf20Sopenharmony_ci u32 data, int in_pm) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u32 buf; 1118c2ecf20Sopenharmony_ci int ret; 1128c2ecf20Sopenharmony_ci int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci BUG_ON(!dev); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!in_pm) 1178c2ecf20Sopenharmony_ci fn = usbnet_write_cmd; 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci fn = usbnet_write_cmd_nopm; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci buf = data; 1228c2ecf20Sopenharmony_ci cpu_to_le32s(&buf); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT 1258c2ecf20Sopenharmony_ci | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1268c2ecf20Sopenharmony_ci 0, index, &buf, 4); 1278c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 1288c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write reg index 0x%08x: %d\n", 1298c2ecf20Sopenharmony_ci index, ret); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int __must_check smsc75xx_read_reg_nopm(struct usbnet *dev, u32 index, 1358c2ecf20Sopenharmony_ci u32 *data) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return __smsc75xx_read_reg(dev, index, data, 1); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int __must_check smsc75xx_write_reg_nopm(struct usbnet *dev, u32 index, 1418c2ecf20Sopenharmony_ci u32 data) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return __smsc75xx_write_reg(dev, index, data, 1); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, 1478c2ecf20Sopenharmony_ci u32 *data) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return __smsc75xx_read_reg(dev, index, data, 0); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, 1538c2ecf20Sopenharmony_ci u32 data) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return __smsc75xx_write_reg(dev, index, data, 0); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* Loop until the read is completed with timeout 1598c2ecf20Sopenharmony_ci * called with phy_mutex held */ 1608c2ecf20Sopenharmony_cistatic __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev, 1618c2ecf20Sopenharmony_ci int in_pm) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 1648c2ecf20Sopenharmony_ci u32 val; 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci do { 1688c2ecf20Sopenharmony_ci ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm); 1698c2ecf20Sopenharmony_ci if (ret < 0) { 1708c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading MII_ACCESS\n"); 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!(val & MII_ACCESS_BUSY)) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci } while (!time_after(jiffies, start_time + HZ)); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return -EIO; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx, 1828c2ecf20Sopenharmony_ci int in_pm) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 1858c2ecf20Sopenharmony_ci u32 val, addr; 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* confirm MII not busy */ 1918c2ecf20Sopenharmony_ci ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); 1928c2ecf20Sopenharmony_ci if (ret < 0) { 1938c2ecf20Sopenharmony_ci netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_read\n"); 1948c2ecf20Sopenharmony_ci goto done; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* set the address, index & direction (read from PHY) */ 1988c2ecf20Sopenharmony_ci phy_id &= dev->mii.phy_id_mask; 1998c2ecf20Sopenharmony_ci idx &= dev->mii.reg_num_mask; 2008c2ecf20Sopenharmony_ci addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) 2018c2ecf20Sopenharmony_ci | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) 2028c2ecf20Sopenharmony_ci | MII_ACCESS_READ | MII_ACCESS_BUSY; 2038c2ecf20Sopenharmony_ci ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); 2048c2ecf20Sopenharmony_ci if (ret < 0) { 2058c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing MII_ACCESS\n"); 2068c2ecf20Sopenharmony_ci goto done; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); 2108c2ecf20Sopenharmony_ci if (ret < 0) { 2118c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); 2128c2ecf20Sopenharmony_ci goto done; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm); 2168c2ecf20Sopenharmony_ci if (ret < 0) { 2178c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading MII_DATA\n"); 2188c2ecf20Sopenharmony_ci goto done; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = (u16)(val & 0xFFFF); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cidone: 2248c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void __smsc75xx_mdio_write(struct net_device *netdev, int phy_id, 2298c2ecf20Sopenharmony_ci int idx, int regval, int in_pm) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 2328c2ecf20Sopenharmony_ci u32 val, addr; 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* confirm MII not busy */ 2388c2ecf20Sopenharmony_ci ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); 2398c2ecf20Sopenharmony_ci if (ret < 0) { 2408c2ecf20Sopenharmony_ci netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_write\n"); 2418c2ecf20Sopenharmony_ci goto done; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci val = regval; 2458c2ecf20Sopenharmony_ci ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm); 2468c2ecf20Sopenharmony_ci if (ret < 0) { 2478c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing MII_DATA\n"); 2488c2ecf20Sopenharmony_ci goto done; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* set the address, index & direction (write to PHY) */ 2528c2ecf20Sopenharmony_ci phy_id &= dev->mii.phy_id_mask; 2538c2ecf20Sopenharmony_ci idx &= dev->mii.reg_num_mask; 2548c2ecf20Sopenharmony_ci addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) 2558c2ecf20Sopenharmony_ci | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) 2568c2ecf20Sopenharmony_ci | MII_ACCESS_WRITE | MII_ACCESS_BUSY; 2578c2ecf20Sopenharmony_ci ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); 2588c2ecf20Sopenharmony_ci if (ret < 0) { 2598c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing MII_ACCESS\n"); 2608c2ecf20Sopenharmony_ci goto done; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); 2648c2ecf20Sopenharmony_ci if (ret < 0) { 2658c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); 2668c2ecf20Sopenharmony_ci goto done; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cidone: 2708c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id, 2748c2ecf20Sopenharmony_ci int idx) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return __smsc75xx_mdio_read(netdev, phy_id, idx, 1); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic void smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id, 2808c2ecf20Sopenharmony_ci int idx, int regval) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci return __smsc75xx_mdio_read(netdev, phy_id, idx, 0); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, 2918c2ecf20Sopenharmony_ci int regval) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int smsc75xx_wait_eeprom(struct usbnet *dev) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 2998c2ecf20Sopenharmony_ci u32 val; 3008c2ecf20Sopenharmony_ci int ret; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci do { 3038c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, E2P_CMD, &val); 3048c2ecf20Sopenharmony_ci if (ret < 0) { 3058c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading E2P_CMD\n"); 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT)) 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci udelay(40); 3128c2ecf20Sopenharmony_ci } while (!time_after(jiffies, start_time + HZ)); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (val & (E2P_CMD_TIMEOUT | E2P_CMD_BUSY)) { 3158c2ecf20Sopenharmony_ci netdev_warn(dev->net, "EEPROM read operation timeout\n"); 3168c2ecf20Sopenharmony_ci return -EIO; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 3258c2ecf20Sopenharmony_ci u32 val; 3268c2ecf20Sopenharmony_ci int ret; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci do { 3298c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, E2P_CMD, &val); 3308c2ecf20Sopenharmony_ci if (ret < 0) { 3318c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading E2P_CMD\n"); 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (!(val & E2P_CMD_BUSY)) 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci udelay(40); 3398c2ecf20Sopenharmony_ci } while (!time_after(jiffies, start_time + HZ)); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci netdev_warn(dev->net, "EEPROM is busy\n"); 3428c2ecf20Sopenharmony_ci return -EIO; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, 3468c2ecf20Sopenharmony_ci u8 *data) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci u32 val; 3498c2ecf20Sopenharmony_ci int i, ret; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci BUG_ON(!dev); 3528c2ecf20Sopenharmony_ci BUG_ON(!data); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = smsc75xx_eeprom_confirm_not_busy(dev); 3558c2ecf20Sopenharmony_ci if (ret) 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 3598c2ecf20Sopenharmony_ci val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR); 3608c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, E2P_CMD, val); 3618c2ecf20Sopenharmony_ci if (ret < 0) { 3628c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing E2P_CMD\n"); 3638c2ecf20Sopenharmony_ci return ret; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = smsc75xx_wait_eeprom(dev); 3678c2ecf20Sopenharmony_ci if (ret < 0) 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, E2P_DATA, &val); 3718c2ecf20Sopenharmony_ci if (ret < 0) { 3728c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading E2P_DATA\n"); 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci data[i] = val & 0xFF; 3778c2ecf20Sopenharmony_ci offset++; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, 3848c2ecf20Sopenharmony_ci u8 *data) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci u32 val; 3878c2ecf20Sopenharmony_ci int i, ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci BUG_ON(!dev); 3908c2ecf20Sopenharmony_ci BUG_ON(!data); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = smsc75xx_eeprom_confirm_not_busy(dev); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Issue write/erase enable command */ 3978c2ecf20Sopenharmony_ci val = E2P_CMD_BUSY | E2P_CMD_EWEN; 3988c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, E2P_CMD, val); 3998c2ecf20Sopenharmony_ci if (ret < 0) { 4008c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing E2P_CMD\n"); 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ret = smsc75xx_wait_eeprom(dev); 4058c2ecf20Sopenharmony_ci if (ret < 0) 4068c2ecf20Sopenharmony_ci return ret; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Fill data register */ 4118c2ecf20Sopenharmony_ci val = data[i]; 4128c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, E2P_DATA, val); 4138c2ecf20Sopenharmony_ci if (ret < 0) { 4148c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing E2P_DATA\n"); 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* Send "write" command */ 4198c2ecf20Sopenharmony_ci val = E2P_CMD_BUSY | E2P_CMD_WRITE | (offset & E2P_CMD_ADDR); 4208c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, E2P_CMD, val); 4218c2ecf20Sopenharmony_ci if (ret < 0) { 4228c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing E2P_CMD\n"); 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = smsc75xx_wait_eeprom(dev); 4278c2ecf20Sopenharmony_ci if (ret < 0) 4288c2ecf20Sopenharmony_ci return ret; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci offset++; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int smsc75xx_dataport_wait_not_busy(struct usbnet *dev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci int i, ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 4418c2ecf20Sopenharmony_ci u32 dp_sel; 4428c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); 4438c2ecf20Sopenharmony_ci if (ret < 0) { 4448c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading DP_SEL\n"); 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (dp_sel & DP_SEL_DPRDY) 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci udelay(40); 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out\n"); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return -EIO; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr, 4608c2ecf20Sopenharmony_ci u32 length, u32 *buf) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 4638c2ecf20Sopenharmony_ci u32 dp_sel; 4648c2ecf20Sopenharmony_ci int i, ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci mutex_lock(&pdata->dataport_mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ret = smsc75xx_dataport_wait_not_busy(dev); 4698c2ecf20Sopenharmony_ci if (ret < 0) { 4708c2ecf20Sopenharmony_ci netdev_warn(dev->net, "smsc75xx_dataport_write busy on entry\n"); 4718c2ecf20Sopenharmony_ci goto done; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); 4758c2ecf20Sopenharmony_ci if (ret < 0) { 4768c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading DP_SEL\n"); 4778c2ecf20Sopenharmony_ci goto done; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dp_sel &= ~DP_SEL_RSEL; 4818c2ecf20Sopenharmony_ci dp_sel |= ram_select; 4828c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, DP_SEL, dp_sel); 4838c2ecf20Sopenharmony_ci if (ret < 0) { 4848c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing DP_SEL\n"); 4858c2ecf20Sopenharmony_ci goto done; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 4898c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, DP_ADDR, addr + i); 4908c2ecf20Sopenharmony_ci if (ret < 0) { 4918c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing DP_ADDR\n"); 4928c2ecf20Sopenharmony_ci goto done; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]); 4968c2ecf20Sopenharmony_ci if (ret < 0) { 4978c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing DP_DATA\n"); 4988c2ecf20Sopenharmony_ci goto done; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE); 5028c2ecf20Sopenharmony_ci if (ret < 0) { 5038c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing DP_CMD\n"); 5048c2ecf20Sopenharmony_ci goto done; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ret = smsc75xx_dataport_wait_not_busy(dev); 5088c2ecf20Sopenharmony_ci if (ret < 0) { 5098c2ecf20Sopenharmony_ci netdev_warn(dev->net, "smsc75xx_dataport_write timeout\n"); 5108c2ecf20Sopenharmony_ci goto done; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cidone: 5158c2ecf20Sopenharmony_ci mutex_unlock(&pdata->dataport_mutex); 5168c2ecf20Sopenharmony_ci return ret; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* returns hash bit number for given MAC address */ 5208c2ecf20Sopenharmony_cistatic u32 smsc75xx_hash(char addr[ETH_ALEN]) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic void smsc75xx_deferred_multicast_write(struct work_struct *param) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = 5288c2ecf20Sopenharmony_ci container_of(param, struct smsc75xx_priv, set_multicast); 5298c2ecf20Sopenharmony_ci struct usbnet *dev = pdata->dev; 5308c2ecf20Sopenharmony_ci int ret; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n", 5338c2ecf20Sopenharmony_ci pdata->rfe_ctl); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci smsc75xx_dataport_write(dev, DP_SEL_VHF, DP_SEL_VHF_VLAN_LEN, 5368c2ecf20Sopenharmony_ci DP_SEL_VHF_HASH_LEN, pdata->multicast_hash_table); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); 5398c2ecf20Sopenharmony_ci if (ret < 0) 5408c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing RFE_CRL\n"); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic void smsc75xx_set_multicast(struct net_device *netdev) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 5468c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 5478c2ecf20Sopenharmony_ci unsigned long flags; 5488c2ecf20Sopenharmony_ci int i; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci pdata->rfe_ctl &= 5538c2ecf20Sopenharmony_ci ~(RFE_CTL_AU | RFE_CTL_AM | RFE_CTL_DPF | RFE_CTL_MHF); 5548c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_AB; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++) 5578c2ecf20Sopenharmony_ci pdata->multicast_hash_table[i] = 0; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (dev->net->flags & IFF_PROMISC) { 5608c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "promiscuous mode enabled\n"); 5618c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_AU; 5628c2ecf20Sopenharmony_ci } else if (dev->net->flags & IFF_ALLMULTI) { 5638c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "receive all multicast enabled\n"); 5648c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_DPF; 5658c2ecf20Sopenharmony_ci } else if (!netdev_mc_empty(dev->net)) { 5668c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "receive multicast hash filter\n"); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 5738c2ecf20Sopenharmony_ci u32 bitnum = smsc75xx_hash(ha->addr); 5748c2ecf20Sopenharmony_ci pdata->multicast_hash_table[bitnum / 32] |= 5758c2ecf20Sopenharmony_ci (1 << (bitnum % 32)); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci } else { 5788c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "receive own packets only\n"); 5798c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_DPF; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* defer register writes to a sleepable context */ 5858c2ecf20Sopenharmony_ci schedule_work(&pdata->set_multicast); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex, 5898c2ecf20Sopenharmony_ci u16 lcladv, u16 rmtadv) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci u32 flow = 0, fct_flow = 0; 5928c2ecf20Sopenharmony_ci int ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) { 5958c2ecf20Sopenharmony_ci u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (cap & FLOW_CTRL_TX) { 5988c2ecf20Sopenharmony_ci flow = (FLOW_TX_FCEN | 0xFFFF); 5998c2ecf20Sopenharmony_ci /* set fct_flow thresholds to 20% and 80% */ 6008c2ecf20Sopenharmony_ci fct_flow = (8 << 8) | 32; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (cap & FLOW_CTRL_RX) 6048c2ecf20Sopenharmony_ci flow |= FLOW_RX_FCEN; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n", 6078c2ecf20Sopenharmony_ci (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), 6088c2ecf20Sopenharmony_ci (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); 6098c2ecf20Sopenharmony_ci } else { 6108c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, "half duplex\n"); 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FLOW, flow); 6148c2ecf20Sopenharmony_ci if (ret < 0) { 6158c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing FLOW\n"); 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow); 6208c2ecf20Sopenharmony_ci if (ret < 0) { 6218c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing FCT_FLOW\n"); 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int smsc75xx_link_reset(struct usbnet *dev) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct mii_if_info *mii = &dev->mii; 6318c2ecf20Sopenharmony_ci struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; 6328c2ecf20Sopenharmony_ci u16 lcladv, rmtadv; 6338c2ecf20Sopenharmony_ci int ret; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* write to clear phy interrupt status */ 6368c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, mii->phy_id, PHY_INT_SRC, 6378c2ecf20Sopenharmony_ci PHY_INT_SRC_CLEAR_ALL); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); 6408c2ecf20Sopenharmony_ci if (ret < 0) { 6418c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing INT_STS\n"); 6428c2ecf20Sopenharmony_ci return ret; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci mii_check_media(mii, 1, 1); 6468c2ecf20Sopenharmony_ci mii_ethtool_gset(&dev->mii, &ecmd); 6478c2ecf20Sopenharmony_ci lcladv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); 6488c2ecf20Sopenharmony_ci rmtadv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_LPA); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", 6518c2ecf20Sopenharmony_ci ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return smsc75xx_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic void smsc75xx_status(struct usbnet *dev, struct urb *urb) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci u32 intdata; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (urb->actual_length != 4) { 6618c2ecf20Sopenharmony_ci netdev_warn(dev->net, "unexpected urb length %d\n", 6628c2ecf20Sopenharmony_ci urb->actual_length); 6638c2ecf20Sopenharmony_ci return; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci intdata = get_unaligned_le32(urb->transfer_buffer); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (intdata & INT_ENP_PHY_INT) 6718c2ecf20Sopenharmony_ci usbnet_defer_kevent(dev, EVENT_LINK_RESET); 6728c2ecf20Sopenharmony_ci else 6738c2ecf20Sopenharmony_ci netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n", 6748c2ecf20Sopenharmony_ci intdata); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic int smsc75xx_ethtool_get_eeprom_len(struct net_device *net) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci return MAX_EEPROM_SIZE; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int smsc75xx_ethtool_get_eeprom(struct net_device *netdev, 6838c2ecf20Sopenharmony_ci struct ethtool_eeprom *ee, u8 *data) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ee->magic = LAN75XX_EEPROM_MAGIC; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return smsc75xx_read_eeprom(dev, ee->offset, ee->len, data); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int smsc75xx_ethtool_set_eeprom(struct net_device *netdev, 6938c2ecf20Sopenharmony_ci struct ethtool_eeprom *ee, u8 *data) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (ee->magic != LAN75XX_EEPROM_MAGIC) { 6988c2ecf20Sopenharmony_ci netdev_warn(dev->net, "EEPROM: magic value mismatch: 0x%x\n", 6998c2ecf20Sopenharmony_ci ee->magic); 7008c2ecf20Sopenharmony_ci return -EINVAL; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic void smsc75xx_ethtool_get_wol(struct net_device *net, 7078c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wolinfo) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 7108c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci wolinfo->supported = SUPPORTED_WAKE; 7138c2ecf20Sopenharmony_ci wolinfo->wolopts = pdata->wolopts; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int smsc75xx_ethtool_set_wol(struct net_device *net, 7178c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wolinfo) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 7208c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 7218c2ecf20Sopenharmony_ci int ret; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (wolinfo->wolopts & ~SUPPORTED_WAKE) 7248c2ecf20Sopenharmony_ci return -EINVAL; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); 7298c2ecf20Sopenharmony_ci if (ret < 0) 7308c2ecf20Sopenharmony_ci netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return ret; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic const struct ethtool_ops smsc75xx_ethtool_ops = { 7368c2ecf20Sopenharmony_ci .get_link = usbnet_get_link, 7378c2ecf20Sopenharmony_ci .nway_reset = usbnet_nway_reset, 7388c2ecf20Sopenharmony_ci .get_drvinfo = usbnet_get_drvinfo, 7398c2ecf20Sopenharmony_ci .get_msglevel = usbnet_get_msglevel, 7408c2ecf20Sopenharmony_ci .set_msglevel = usbnet_set_msglevel, 7418c2ecf20Sopenharmony_ci .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, 7428c2ecf20Sopenharmony_ci .get_eeprom = smsc75xx_ethtool_get_eeprom, 7438c2ecf20Sopenharmony_ci .set_eeprom = smsc75xx_ethtool_set_eeprom, 7448c2ecf20Sopenharmony_ci .get_wol = smsc75xx_ethtool_get_wol, 7458c2ecf20Sopenharmony_ci .set_wol = smsc75xx_ethtool_set_wol, 7468c2ecf20Sopenharmony_ci .get_link_ksettings = usbnet_get_link_ksettings, 7478c2ecf20Sopenharmony_ci .set_link_ksettings = usbnet_set_link_ksettings, 7488c2ecf20Sopenharmony_ci}; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 7558c2ecf20Sopenharmony_ci return -EINVAL; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic void smsc75xx_init_mac_address(struct usbnet *dev) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci /* maybe the boot loader passed the MAC address in devicetree */ 7638c2ecf20Sopenharmony_ci if (!eth_platform_get_mac_address(&dev->udev->dev, 7648c2ecf20Sopenharmony_ci dev->net->dev_addr)) { 7658c2ecf20Sopenharmony_ci if (is_valid_ether_addr(dev->net->dev_addr)) { 7668c2ecf20Sopenharmony_ci /* device tree values are valid so use them */ 7678c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "MAC address read from the device tree\n"); 7688c2ecf20Sopenharmony_ci return; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* try reading mac address from EEPROM */ 7738c2ecf20Sopenharmony_ci if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, 7748c2ecf20Sopenharmony_ci dev->net->dev_addr) == 0) { 7758c2ecf20Sopenharmony_ci if (is_valid_ether_addr(dev->net->dev_addr)) { 7768c2ecf20Sopenharmony_ci /* eeprom values are valid so use them */ 7778c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, 7788c2ecf20Sopenharmony_ci "MAC address read from EEPROM\n"); 7798c2ecf20Sopenharmony_ci return; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* no useful static MAC address found. generate a random one */ 7848c2ecf20Sopenharmony_ci eth_hw_addr_random(dev->net); 7858c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int smsc75xx_set_mac_address(struct usbnet *dev) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | 7918c2ecf20Sopenharmony_ci dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24; 7928c2ecf20Sopenharmony_ci u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi); 7958c2ecf20Sopenharmony_ci if (ret < 0) { 7968c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write RX_ADDRH: %d\n", ret); 7978c2ecf20Sopenharmony_ci return ret; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, RX_ADDRL, addr_lo); 8018c2ecf20Sopenharmony_ci if (ret < 0) { 8028c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write RX_ADDRL: %d\n", ret); 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci addr_hi |= ADDR_FILTX_FB_VALID; 8078c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, ADDR_FILTX, addr_hi); 8088c2ecf20Sopenharmony_ci if (ret < 0) { 8098c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write ADDR_FILTX: %d\n", ret); 8108c2ecf20Sopenharmony_ci return ret; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, ADDR_FILTX + 4, addr_lo); 8148c2ecf20Sopenharmony_ci if (ret < 0) 8158c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write ADDR_FILTX+4: %d\n", ret); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return ret; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic int smsc75xx_phy_initialize(struct usbnet *dev) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci int bmcr, ret, timeout = 0; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci /* Initialize MII structure */ 8258c2ecf20Sopenharmony_ci dev->mii.dev = dev->net; 8268c2ecf20Sopenharmony_ci dev->mii.mdio_read = smsc75xx_mdio_read; 8278c2ecf20Sopenharmony_ci dev->mii.mdio_write = smsc75xx_mdio_write; 8288c2ecf20Sopenharmony_ci dev->mii.phy_id_mask = 0x1f; 8298c2ecf20Sopenharmony_ci dev->mii.reg_num_mask = 0x1f; 8308c2ecf20Sopenharmony_ci dev->mii.supports_gmii = 1; 8318c2ecf20Sopenharmony_ci dev->mii.phy_id = SMSC75XX_INTERNAL_PHY_ID; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* reset phy and wait for reset to complete */ 8348c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci do { 8378c2ecf20Sopenharmony_ci msleep(10); 8388c2ecf20Sopenharmony_ci bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); 8398c2ecf20Sopenharmony_ci if (bmcr < 0) { 8408c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading MII_BMCR\n"); 8418c2ecf20Sopenharmony_ci return bmcr; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci timeout++; 8448c2ecf20Sopenharmony_ci } while ((bmcr & BMCR_RESET) && (timeout < 100)); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (timeout >= 100) { 8478c2ecf20Sopenharmony_ci netdev_warn(dev->net, "timeout on PHY Reset\n"); 8488c2ecf20Sopenharmony_ci return -EIO; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* phy workaround for gig link */ 8528c2ecf20Sopenharmony_ci smsc75xx_phy_gig_workaround(dev); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, 8558c2ecf20Sopenharmony_ci ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | 8568c2ecf20Sopenharmony_ci ADVERTISE_PAUSE_ASYM); 8578c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, 8588c2ecf20Sopenharmony_ci ADVERTISE_1000FULL); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* read and write to clear phy interrupt status */ 8618c2ecf20Sopenharmony_ci ret = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); 8628c2ecf20Sopenharmony_ci if (ret < 0) { 8638c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); 8648c2ecf20Sopenharmony_ci return ret; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_SRC, 0xffff); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, 8708c2ecf20Sopenharmony_ci PHY_INT_MASK_DEFAULT); 8718c2ecf20Sopenharmony_ci mii_nway_restart(&dev->mii); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci int ret = 0; 8808c2ecf20Sopenharmony_ci u32 buf; 8818c2ecf20Sopenharmony_ci bool rxenabled; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, MAC_RX, &buf); 8848c2ecf20Sopenharmony_ci if (ret < 0) { 8858c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); 8868c2ecf20Sopenharmony_ci return ret; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci rxenabled = ((buf & MAC_RX_RXEN) != 0); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (rxenabled) { 8928c2ecf20Sopenharmony_ci buf &= ~MAC_RX_RXEN; 8938c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, MAC_RX, buf); 8948c2ecf20Sopenharmony_ci if (ret < 0) { 8958c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); 8968c2ecf20Sopenharmony_ci return ret; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* add 4 to size for FCS */ 9018c2ecf20Sopenharmony_ci buf &= ~MAC_RX_MAX_SIZE; 9028c2ecf20Sopenharmony_ci buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, MAC_RX, buf); 9058c2ecf20Sopenharmony_ci if (ret < 0) { 9068c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); 9078c2ecf20Sopenharmony_ci return ret; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (rxenabled) { 9118c2ecf20Sopenharmony_ci buf |= MAC_RX_RXEN; 9128c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, MAC_RX, buf); 9138c2ecf20Sopenharmony_ci if (ret < 0) { 9148c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); 9158c2ecf20Sopenharmony_ci return ret; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 9258c2ecf20Sopenharmony_ci int ret; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu + ETH_HLEN); 9288c2ecf20Sopenharmony_ci if (ret < 0) { 9298c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to set mac rx frame length\n"); 9308c2ecf20Sopenharmony_ci return ret; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci return usbnet_change_mtu(netdev, new_mtu); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci/* Enable or disable Rx checksum offload engine */ 9378c2ecf20Sopenharmony_cistatic int smsc75xx_set_features(struct net_device *netdev, 9388c2ecf20Sopenharmony_ci netdev_features_t features) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct usbnet *dev = netdev_priv(netdev); 9418c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 9428c2ecf20Sopenharmony_ci unsigned long flags; 9438c2ecf20Sopenharmony_ci int ret; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (features & NETIF_F_RXCSUM) 9488c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM; 9498c2ecf20Sopenharmony_ci else 9508c2ecf20Sopenharmony_ci pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); 9538c2ecf20Sopenharmony_ci /* it's racing here! */ 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); 9568c2ecf20Sopenharmony_ci if (ret < 0) { 9578c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing RFE_CTL\n"); 9588c2ecf20Sopenharmony_ci return ret; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci return 0; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci int timeout = 0; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci do { 9688c2ecf20Sopenharmony_ci u32 buf; 9698c2ecf20Sopenharmony_ci int ret; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci ret = __smsc75xx_read_reg(dev, PMT_CTL, &buf, in_pm); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (ret < 0) { 9748c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); 9758c2ecf20Sopenharmony_ci return ret; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (buf & PMT_CTL_DEV_RDY) 9798c2ecf20Sopenharmony_ci return 0; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci msleep(10); 9828c2ecf20Sopenharmony_ci timeout++; 9838c2ecf20Sopenharmony_ci } while (timeout < 100); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci netdev_warn(dev->net, "timeout waiting for device ready\n"); 9868c2ecf20Sopenharmony_ci return -EIO; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int smsc75xx_phy_gig_workaround(struct usbnet *dev) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct mii_if_info *mii = &dev->mii; 9928c2ecf20Sopenharmony_ci int ret = 0, timeout = 0; 9938c2ecf20Sopenharmony_ci u32 buf, link_up = 0; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* Set the phy in Gig loopback */ 9968c2ecf20Sopenharmony_ci smsc75xx_mdio_write(dev->net, mii->phy_id, MII_BMCR, 0x4040); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* Wait for the link up */ 9998c2ecf20Sopenharmony_ci do { 10008c2ecf20Sopenharmony_ci link_up = smsc75xx_link_ok_nopm(dev); 10018c2ecf20Sopenharmony_ci usleep_range(10000, 20000); 10028c2ecf20Sopenharmony_ci timeout++; 10038c2ecf20Sopenharmony_ci } while ((!link_up) && (timeout < 1000)); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (timeout >= 1000) { 10068c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Timeout waiting for PHY link up\n"); 10078c2ecf20Sopenharmony_ci return -EIO; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* phy reset */ 10118c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); 10128c2ecf20Sopenharmony_ci if (ret < 0) { 10138c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); 10148c2ecf20Sopenharmony_ci return ret; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci buf |= PMT_CTL_PHY_RST; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, PMT_CTL, buf); 10208c2ecf20Sopenharmony_ci if (ret < 0) { 10218c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci timeout = 0; 10268c2ecf20Sopenharmony_ci do { 10278c2ecf20Sopenharmony_ci usleep_range(10000, 20000); 10288c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); 10298c2ecf20Sopenharmony_ci if (ret < 0) { 10308c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", 10318c2ecf20Sopenharmony_ci ret); 10328c2ecf20Sopenharmony_ci return ret; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci timeout++; 10358c2ecf20Sopenharmony_ci } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (timeout >= 100) { 10388c2ecf20Sopenharmony_ci netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); 10398c2ecf20Sopenharmony_ci return -EIO; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return 0; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic int smsc75xx_reset(struct usbnet *dev) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 10488c2ecf20Sopenharmony_ci u32 buf; 10498c2ecf20Sopenharmony_ci int ret = 0, timeout; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset\n"); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci ret = smsc75xx_wait_ready(dev, 0); 10548c2ecf20Sopenharmony_ci if (ret < 0) { 10558c2ecf20Sopenharmony_ci netdev_warn(dev->net, "device not ready in smsc75xx_reset\n"); 10568c2ecf20Sopenharmony_ci return ret; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, HW_CFG, &buf); 10608c2ecf20Sopenharmony_ci if (ret < 0) { 10618c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); 10628c2ecf20Sopenharmony_ci return ret; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci buf |= HW_CFG_LRST; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, HW_CFG, buf); 10688c2ecf20Sopenharmony_ci if (ret < 0) { 10698c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); 10708c2ecf20Sopenharmony_ci return ret; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci timeout = 0; 10748c2ecf20Sopenharmony_ci do { 10758c2ecf20Sopenharmony_ci msleep(10); 10768c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, HW_CFG, &buf); 10778c2ecf20Sopenharmony_ci if (ret < 0) { 10788c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); 10798c2ecf20Sopenharmony_ci return ret; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci timeout++; 10828c2ecf20Sopenharmony_ci } while ((buf & HW_CFG_LRST) && (timeout < 100)); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (timeout >= 100) { 10858c2ecf20Sopenharmony_ci netdev_warn(dev->net, "timeout on completion of Lite Reset\n"); 10868c2ecf20Sopenharmony_ci return -EIO; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY\n"); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); 10928c2ecf20Sopenharmony_ci if (ret < 0) { 10938c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); 10948c2ecf20Sopenharmony_ci return ret; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci buf |= PMT_CTL_PHY_RST; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, PMT_CTL, buf); 11008c2ecf20Sopenharmony_ci if (ret < 0) { 11018c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); 11028c2ecf20Sopenharmony_ci return ret; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci timeout = 0; 11068c2ecf20Sopenharmony_ci do { 11078c2ecf20Sopenharmony_ci msleep(10); 11088c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); 11098c2ecf20Sopenharmony_ci if (ret < 0) { 11108c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); 11118c2ecf20Sopenharmony_ci return ret; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci timeout++; 11148c2ecf20Sopenharmony_ci } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (timeout >= 100) { 11178c2ecf20Sopenharmony_ci netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); 11188c2ecf20Sopenharmony_ci return -EIO; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "PHY reset complete\n"); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ret = smsc75xx_set_mac_address(dev); 11248c2ecf20Sopenharmony_ci if (ret < 0) { 11258c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to set mac address\n"); 11268c2ecf20Sopenharmony_ci return ret; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", 11308c2ecf20Sopenharmony_ci dev->net->dev_addr); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, HW_CFG, &buf); 11338c2ecf20Sopenharmony_ci if (ret < 0) { 11348c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); 11358c2ecf20Sopenharmony_ci return ret; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", 11398c2ecf20Sopenharmony_ci buf); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci buf |= HW_CFG_BIR; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, HW_CFG, buf); 11448c2ecf20Sopenharmony_ci if (ret < 0) { 11458c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); 11468c2ecf20Sopenharmony_ci return ret; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, HW_CFG, &buf); 11508c2ecf20Sopenharmony_ci if (ret < 0) { 11518c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); 11528c2ecf20Sopenharmony_ci return ret; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing HW_CFG_BIR: 0x%08x\n", 11568c2ecf20Sopenharmony_ci buf); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (!turbo_mode) { 11598c2ecf20Sopenharmony_ci buf = 0; 11608c2ecf20Sopenharmony_ci dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; 11618c2ecf20Sopenharmony_ci } else if (dev->udev->speed == USB_SPEED_HIGH) { 11628c2ecf20Sopenharmony_ci buf = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; 11638c2ecf20Sopenharmony_ci dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; 11648c2ecf20Sopenharmony_ci } else { 11658c2ecf20Sopenharmony_ci buf = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; 11668c2ecf20Sopenharmony_ci dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", 11708c2ecf20Sopenharmony_ci (ulong)dev->rx_urb_size); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, BURST_CAP, buf); 11738c2ecf20Sopenharmony_ci if (ret < 0) { 11748c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); 11758c2ecf20Sopenharmony_ci return ret; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, BURST_CAP, &buf); 11798c2ecf20Sopenharmony_ci if (ret < 0) { 11808c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); 11818c2ecf20Sopenharmony_ci return ret; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, 11858c2ecf20Sopenharmony_ci "Read Value from BURST_CAP after writing: 0x%08x\n", buf); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); 11888c2ecf20Sopenharmony_ci if (ret < 0) { 11898c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write BULK_IN_DLY: %d\n", ret); 11908c2ecf20Sopenharmony_ci return ret; 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, BULK_IN_DLY, &buf); 11948c2ecf20Sopenharmony_ci if (ret < 0) { 11958c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); 11968c2ecf20Sopenharmony_ci return ret; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, 12008c2ecf20Sopenharmony_ci "Read Value from BULK_IN_DLY after writing: 0x%08x\n", buf); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (turbo_mode) { 12038c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, HW_CFG, &buf); 12048c2ecf20Sopenharmony_ci if (ret < 0) { 12058c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); 12068c2ecf20Sopenharmony_ci return ret; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci buf |= (HW_CFG_MEF | HW_CFG_BCE); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, HW_CFG, buf); 12148c2ecf20Sopenharmony_ci if (ret < 0) { 12158c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); 12168c2ecf20Sopenharmony_ci return ret; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, HW_CFG, &buf); 12208c2ecf20Sopenharmony_ci if (ret < 0) { 12218c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); 12228c2ecf20Sopenharmony_ci return ret; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* set FIFO sizes */ 12298c2ecf20Sopenharmony_ci buf = (MAX_RX_FIFO_SIZE - 512) / 512; 12308c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FCT_RX_FIFO_END, buf); 12318c2ecf20Sopenharmony_ci if (ret < 0) { 12328c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write FCT_RX_FIFO_END: %d\n", ret); 12338c2ecf20Sopenharmony_ci return ret; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "FCT_RX_FIFO_END set to 0x%08x\n", buf); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci buf = (MAX_TX_FIFO_SIZE - 512) / 512; 12398c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FCT_TX_FIFO_END, buf); 12408c2ecf20Sopenharmony_ci if (ret < 0) { 12418c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write FCT_TX_FIFO_END: %d\n", ret); 12428c2ecf20Sopenharmony_ci return ret; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "FCT_TX_FIFO_END set to 0x%08x\n", buf); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); 12488c2ecf20Sopenharmony_ci if (ret < 0) { 12498c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write INT_STS: %d\n", ret); 12508c2ecf20Sopenharmony_ci return ret; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, ID_REV, &buf); 12548c2ecf20Sopenharmony_ci if (ret < 0) { 12558c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); 12568c2ecf20Sopenharmony_ci return ret; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", buf); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, E2P_CMD, &buf); 12628c2ecf20Sopenharmony_ci if (ret < 0) { 12638c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read E2P_CMD: %d\n", ret); 12648c2ecf20Sopenharmony_ci return ret; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci /* only set default GPIO/LED settings if no EEPROM is detected */ 12688c2ecf20Sopenharmony_ci if (!(buf & E2P_CMD_LOADED)) { 12698c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); 12708c2ecf20Sopenharmony_ci if (ret < 0) { 12718c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read LED_GPIO_CFG: %d\n", ret); 12728c2ecf20Sopenharmony_ci return ret; 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci buf &= ~(LED_GPIO_CFG_LED2_FUN_SEL | LED_GPIO_CFG_LED10_FUN_SEL); 12768c2ecf20Sopenharmony_ci buf |= LED_GPIO_CFG_LEDGPIO_EN | LED_GPIO_CFG_LED2_FUN_SEL; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf); 12798c2ecf20Sopenharmony_ci if (ret < 0) { 12808c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write LED_GPIO_CFG: %d\n", ret); 12818c2ecf20Sopenharmony_ci return ret; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FLOW, 0); 12868c2ecf20Sopenharmony_ci if (ret < 0) { 12878c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); 12888c2ecf20Sopenharmony_ci return ret; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FCT_FLOW, 0); 12928c2ecf20Sopenharmony_ci if (ret < 0) { 12938c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write FCT_FLOW: %d\n", ret); 12948c2ecf20Sopenharmony_ci return ret; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* Don't need rfe_ctl_lock during initialisation */ 12988c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); 12998c2ecf20Sopenharmony_ci if (ret < 0) { 13008c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); 13018c2ecf20Sopenharmony_ci return ret; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_AB | RFE_CTL_DPF; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); 13078c2ecf20Sopenharmony_ci if (ret < 0) { 13088c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write RFE_CTL: %d\n", ret); 13098c2ecf20Sopenharmony_ci return ret; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); 13138c2ecf20Sopenharmony_ci if (ret < 0) { 13148c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); 13158c2ecf20Sopenharmony_ci return ret; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x\n", 13198c2ecf20Sopenharmony_ci pdata->rfe_ctl); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci /* Enable or disable checksum offload engines */ 13228c2ecf20Sopenharmony_ci smsc75xx_set_features(dev->net, dev->net->features); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci smsc75xx_set_multicast(dev->net); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci ret = smsc75xx_phy_initialize(dev); 13278c2ecf20Sopenharmony_ci if (ret < 0) { 13288c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to initialize PHY: %d\n", ret); 13298c2ecf20Sopenharmony_ci return ret; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, INT_EP_CTL, &buf); 13338c2ecf20Sopenharmony_ci if (ret < 0) { 13348c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); 13358c2ecf20Sopenharmony_ci return ret; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci /* enable PHY interrupts */ 13398c2ecf20Sopenharmony_ci buf |= INT_ENP_PHY_INT; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); 13428c2ecf20Sopenharmony_ci if (ret < 0) { 13438c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); 13448c2ecf20Sopenharmony_ci return ret; 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci /* allow mac to detect speed and duplex from phy */ 13488c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, MAC_CR, &buf); 13498c2ecf20Sopenharmony_ci if (ret < 0) { 13508c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); 13518c2ecf20Sopenharmony_ci return ret; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci buf |= (MAC_CR_ADD | MAC_CR_ASD); 13558c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, MAC_CR, buf); 13568c2ecf20Sopenharmony_ci if (ret < 0) { 13578c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); 13588c2ecf20Sopenharmony_ci return ret; 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, MAC_TX, &buf); 13628c2ecf20Sopenharmony_ci if (ret < 0) { 13638c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read MAC_TX: %d\n", ret); 13648c2ecf20Sopenharmony_ci return ret; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci buf |= MAC_TX_TXEN; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, MAC_TX, buf); 13708c2ecf20Sopenharmony_ci if (ret < 0) { 13718c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_TX: %d\n", ret); 13728c2ecf20Sopenharmony_ci return ret; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); 13788c2ecf20Sopenharmony_ci if (ret < 0) { 13798c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read FCT_TX_CTL: %d\n", ret); 13808c2ecf20Sopenharmony_ci return ret; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci buf |= FCT_TX_CTL_EN; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf); 13868c2ecf20Sopenharmony_ci if (ret < 0) { 13878c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write FCT_TX_CTL: %d\n", ret); 13888c2ecf20Sopenharmony_ci return ret; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci ret = smsc75xx_set_rx_max_frame_length(dev, dev->net->mtu + ETH_HLEN); 13948c2ecf20Sopenharmony_ci if (ret < 0) { 13958c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to set max rx frame length\n"); 13968c2ecf20Sopenharmony_ci return ret; 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, MAC_RX, &buf); 14008c2ecf20Sopenharmony_ci if (ret < 0) { 14018c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); 14028c2ecf20Sopenharmony_ci return ret; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci buf |= MAC_RX_RXEN; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, MAC_RX, buf); 14088c2ecf20Sopenharmony_ci if (ret < 0) { 14098c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); 14108c2ecf20Sopenharmony_ci return ret; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); 14168c2ecf20Sopenharmony_ci if (ret < 0) { 14178c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read FCT_RX_CTL: %d\n", ret); 14188c2ecf20Sopenharmony_ci return ret; 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci buf |= FCT_RX_CTL_EN; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf); 14248c2ecf20Sopenharmony_ci if (ret < 0) { 14258c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write FCT_RX_CTL: %d\n", ret); 14268c2ecf20Sopenharmony_ci return ret; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x\n", buf); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "smsc75xx_reset, return 0\n"); 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_cistatic const struct net_device_ops smsc75xx_netdev_ops = { 14368c2ecf20Sopenharmony_ci .ndo_open = usbnet_open, 14378c2ecf20Sopenharmony_ci .ndo_stop = usbnet_stop, 14388c2ecf20Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 14398c2ecf20Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 14408c2ecf20Sopenharmony_ci .ndo_get_stats64 = usbnet_get_stats64, 14418c2ecf20Sopenharmony_ci .ndo_change_mtu = smsc75xx_change_mtu, 14428c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 14438c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 14448c2ecf20Sopenharmony_ci .ndo_do_ioctl = smsc75xx_ioctl, 14458c2ecf20Sopenharmony_ci .ndo_set_rx_mode = smsc75xx_set_multicast, 14468c2ecf20Sopenharmony_ci .ndo_set_features = smsc75xx_set_features, 14478c2ecf20Sopenharmony_ci}; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) 14508c2ecf20Sopenharmony_ci{ 14518c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = NULL; 14528c2ecf20Sopenharmony_ci int ret; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci ret = usbnet_get_endpoints(dev, intf); 14578c2ecf20Sopenharmony_ci if (ret < 0) { 14588c2ecf20Sopenharmony_ci netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); 14598c2ecf20Sopenharmony_ci return ret; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc75xx_priv), 14638c2ecf20Sopenharmony_ci GFP_KERNEL); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci pdata = (struct smsc75xx_priv *)(dev->data[0]); 14668c2ecf20Sopenharmony_ci if (!pdata) 14678c2ecf20Sopenharmony_ci return -ENOMEM; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci pdata->dev = dev; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci spin_lock_init(&pdata->rfe_ctl_lock); 14728c2ecf20Sopenharmony_ci mutex_init(&pdata->dataport_mutex); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci if (DEFAULT_TX_CSUM_ENABLE) 14778c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (DEFAULT_RX_CSUM_ENABLE) 14808c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_RXCSUM; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 14838c2ecf20Sopenharmony_ci NETIF_F_RXCSUM; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci ret = smsc75xx_wait_ready(dev, 0); 14868c2ecf20Sopenharmony_ci if (ret < 0) { 14878c2ecf20Sopenharmony_ci netdev_warn(dev->net, "device not ready in smsc75xx_bind\n"); 14888c2ecf20Sopenharmony_ci goto free_pdata; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci smsc75xx_init_mac_address(dev); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci /* Init all registers */ 14948c2ecf20Sopenharmony_ci ret = smsc75xx_reset(dev); 14958c2ecf20Sopenharmony_ci if (ret < 0) { 14968c2ecf20Sopenharmony_ci netdev_warn(dev->net, "smsc75xx_reset error %d\n", ret); 14978c2ecf20Sopenharmony_ci goto cancel_work; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci dev->net->netdev_ops = &smsc75xx_netdev_ops; 15018c2ecf20Sopenharmony_ci dev->net->ethtool_ops = &smsc75xx_ethtool_ops; 15028c2ecf20Sopenharmony_ci dev->net->flags |= IFF_MULTICAST; 15038c2ecf20Sopenharmony_ci dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD; 15048c2ecf20Sopenharmony_ci dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 15058c2ecf20Sopenharmony_ci dev->net->max_mtu = MAX_SINGLE_PACKET_SIZE; 15068c2ecf20Sopenharmony_ci return 0; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cicancel_work: 15098c2ecf20Sopenharmony_ci cancel_work_sync(&pdata->set_multicast); 15108c2ecf20Sopenharmony_cifree_pdata: 15118c2ecf20Sopenharmony_ci kfree(pdata); 15128c2ecf20Sopenharmony_ci dev->data[0] = 0; 15138c2ecf20Sopenharmony_ci return ret; 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 15198c2ecf20Sopenharmony_ci if (pdata) { 15208c2ecf20Sopenharmony_ci cancel_work_sync(&pdata->set_multicast); 15218c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, "free pdata\n"); 15228c2ecf20Sopenharmony_ci kfree(pdata); 15238c2ecf20Sopenharmony_ci dev->data[0] = 0; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic u16 smsc_crc(const u8 *buffer, size_t len) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci return bitrev16(crc16(0xFFFF, buffer, len)); 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg, 15338c2ecf20Sopenharmony_ci u32 wuf_mask1) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci int cfg_base = WUF_CFGX + filter * 4; 15368c2ecf20Sopenharmony_ci int mask_base = WUF_MASKX + filter * 16; 15378c2ecf20Sopenharmony_ci int ret; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, cfg_base, wuf_cfg); 15408c2ecf20Sopenharmony_ci if (ret < 0) { 15418c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUF_CFGX\n"); 15428c2ecf20Sopenharmony_ci return ret; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1); 15468c2ecf20Sopenharmony_ci if (ret < 0) { 15478c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUF_MASKX\n"); 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, mask_base + 4, 0); 15528c2ecf20Sopenharmony_ci if (ret < 0) { 15538c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUF_MASKX\n"); 15548c2ecf20Sopenharmony_ci return ret; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, mask_base + 8, 0); 15588c2ecf20Sopenharmony_ci if (ret < 0) { 15598c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUF_MASKX\n"); 15608c2ecf20Sopenharmony_ci return ret; 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg(dev, mask_base + 12, 0); 15648c2ecf20Sopenharmony_ci if (ret < 0) { 15658c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUF_MASKX\n"); 15668c2ecf20Sopenharmony_ci return ret; 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci return 0; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistatic int smsc75xx_enter_suspend0(struct usbnet *dev) 15738c2ecf20Sopenharmony_ci{ 15748c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 15758c2ecf20Sopenharmony_ci u32 val; 15768c2ecf20Sopenharmony_ci int ret; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 15798c2ecf20Sopenharmony_ci if (ret < 0) { 15808c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 15818c2ecf20Sopenharmony_ci return ret; 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST)); 15858c2ecf20Sopenharmony_ci val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 15888c2ecf20Sopenharmony_ci if (ret < 0) { 15898c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 15908c2ecf20Sopenharmony_ci return ret; 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci pdata->suspend_flags |= SUSPEND_SUSPEND0; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci return 0; 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic int smsc75xx_enter_suspend1(struct usbnet *dev) 15998c2ecf20Sopenharmony_ci{ 16008c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 16018c2ecf20Sopenharmony_ci u32 val; 16028c2ecf20Sopenharmony_ci int ret; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 16058c2ecf20Sopenharmony_ci if (ret < 0) { 16068c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 16078c2ecf20Sopenharmony_ci return ret; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); 16118c2ecf20Sopenharmony_ci val |= PMT_CTL_SUS_MODE_1; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 16148c2ecf20Sopenharmony_ci if (ret < 0) { 16158c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 16168c2ecf20Sopenharmony_ci return ret; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* clear wol status, enable energy detection */ 16208c2ecf20Sopenharmony_ci val &= ~PMT_CTL_WUPS; 16218c2ecf20Sopenharmony_ci val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 16248c2ecf20Sopenharmony_ci if (ret < 0) { 16258c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 16268c2ecf20Sopenharmony_ci return ret; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci pdata->suspend_flags |= SUSPEND_SUSPEND1; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci return 0; 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_cistatic int smsc75xx_enter_suspend2(struct usbnet *dev) 16358c2ecf20Sopenharmony_ci{ 16368c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 16378c2ecf20Sopenharmony_ci u32 val; 16388c2ecf20Sopenharmony_ci int ret; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 16418c2ecf20Sopenharmony_ci if (ret < 0) { 16428c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 16438c2ecf20Sopenharmony_ci return ret; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); 16478c2ecf20Sopenharmony_ci val |= PMT_CTL_SUS_MODE_2; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 16508c2ecf20Sopenharmony_ci if (ret < 0) { 16518c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 16528c2ecf20Sopenharmony_ci return ret; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci pdata->suspend_flags |= SUSPEND_SUSPEND2; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci return 0; 16588c2ecf20Sopenharmony_ci} 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_cistatic int smsc75xx_enter_suspend3(struct usbnet *dev) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 16638c2ecf20Sopenharmony_ci u32 val; 16648c2ecf20Sopenharmony_ci int ret; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val); 16678c2ecf20Sopenharmony_ci if (ret < 0) { 16688c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading FCT_RX_CTL\n"); 16698c2ecf20Sopenharmony_ci return ret; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (val & FCT_RX_CTL_RXUSED) { 16738c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n"); 16748c2ecf20Sopenharmony_ci return -EBUSY; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 16788c2ecf20Sopenharmony_ci if (ret < 0) { 16798c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 16808c2ecf20Sopenharmony_ci return ret; 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); 16848c2ecf20Sopenharmony_ci val |= PMT_CTL_SUS_MODE_3 | PMT_CTL_RES_CLR_WKP_EN; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 16878c2ecf20Sopenharmony_ci if (ret < 0) { 16888c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 16898c2ecf20Sopenharmony_ci return ret; 16908c2ecf20Sopenharmony_ci } 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci /* clear wol status */ 16938c2ecf20Sopenharmony_ci val &= ~PMT_CTL_WUPS; 16948c2ecf20Sopenharmony_ci val |= PMT_CTL_WUPS_WOL; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 16978c2ecf20Sopenharmony_ci if (ret < 0) { 16988c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 16998c2ecf20Sopenharmony_ci return ret; 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci pdata->suspend_flags |= SUSPEND_SUSPEND3; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci return 0; 17058c2ecf20Sopenharmony_ci} 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_cistatic int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) 17088c2ecf20Sopenharmony_ci{ 17098c2ecf20Sopenharmony_ci struct mii_if_info *mii = &dev->mii; 17108c2ecf20Sopenharmony_ci int ret; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* read to clear */ 17158c2ecf20Sopenharmony_ci ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); 17168c2ecf20Sopenharmony_ci if (ret < 0) { 17178c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); 17188c2ecf20Sopenharmony_ci return ret; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* enable interrupt source */ 17228c2ecf20Sopenharmony_ci ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); 17238c2ecf20Sopenharmony_ci if (ret < 0) { 17248c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PHY_INT_MASK\n"); 17258c2ecf20Sopenharmony_ci return ret; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci ret |= mask; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci return 0; 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_cistatic int smsc75xx_link_ok_nopm(struct usbnet *dev) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct mii_if_info *mii = &dev->mii; 17388c2ecf20Sopenharmony_ci int ret; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci /* first, a dummy read, needed to latch some MII phys */ 17418c2ecf20Sopenharmony_ci ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); 17428c2ecf20Sopenharmony_ci if (ret < 0) { 17438c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading MII_BMSR\n"); 17448c2ecf20Sopenharmony_ci return ret; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); 17488c2ecf20Sopenharmony_ci if (ret < 0) { 17498c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading MII_BMSR\n"); 17508c2ecf20Sopenharmony_ci return ret; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci return !!(ret & BMSR_LSTATUS); 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_cistatic int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci int ret; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (!netif_running(dev->net)) { 17618c2ecf20Sopenharmony_ci /* interface is ifconfig down so fully power down hw */ 17628c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); 17638c2ecf20Sopenharmony_ci return smsc75xx_enter_suspend2(dev); 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci if (!link_up) { 17678c2ecf20Sopenharmony_ci /* link is down so enter EDPD mode */ 17688c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n"); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci /* enable PHY wakeup events for if cable is attached */ 17718c2ecf20Sopenharmony_ci ret = smsc75xx_enable_phy_wakeup_interrupts(dev, 17728c2ecf20Sopenharmony_ci PHY_INT_MASK_ANEG_COMP); 17738c2ecf20Sopenharmony_ci if (ret < 0) { 17748c2ecf20Sopenharmony_ci netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); 17758c2ecf20Sopenharmony_ci return ret; 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci netdev_info(dev->net, "entering SUSPEND1 mode\n"); 17798c2ecf20Sopenharmony_ci return smsc75xx_enter_suspend1(dev); 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci /* enable PHY wakeup events so we remote wakeup if cable is pulled */ 17838c2ecf20Sopenharmony_ci ret = smsc75xx_enable_phy_wakeup_interrupts(dev, 17848c2ecf20Sopenharmony_ci PHY_INT_MASK_LINK_DOWN); 17858c2ecf20Sopenharmony_ci if (ret < 0) { 17868c2ecf20Sopenharmony_ci netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); 17878c2ecf20Sopenharmony_ci return ret; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n"); 17918c2ecf20Sopenharmony_ci return smsc75xx_enter_suspend3(dev); 17928c2ecf20Sopenharmony_ci} 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_cistatic int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) 17958c2ecf20Sopenharmony_ci{ 17968c2ecf20Sopenharmony_ci struct usbnet *dev = usb_get_intfdata(intf); 17978c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 17988c2ecf20Sopenharmony_ci u32 val, link_up; 17998c2ecf20Sopenharmony_ci int ret; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci ret = usbnet_suspend(intf, message); 18028c2ecf20Sopenharmony_ci if (ret < 0) { 18038c2ecf20Sopenharmony_ci netdev_warn(dev->net, "usbnet_suspend error\n"); 18048c2ecf20Sopenharmony_ci return ret; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci if (pdata->suspend_flags) { 18088c2ecf20Sopenharmony_ci netdev_warn(dev->net, "error during last resume\n"); 18098c2ecf20Sopenharmony_ci pdata->suspend_flags = 0; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci /* determine if link is up using only _nopm functions */ 18138c2ecf20Sopenharmony_ci link_up = smsc75xx_link_ok_nopm(dev); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (message.event == PM_EVENT_AUTO_SUSPEND) { 18168c2ecf20Sopenharmony_ci ret = smsc75xx_autosuspend(dev, link_up); 18178c2ecf20Sopenharmony_ci goto done; 18188c2ecf20Sopenharmony_ci } 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci /* if we get this far we're not autosuspending */ 18218c2ecf20Sopenharmony_ci /* if no wol options set, or if link is down and we're not waking on 18228c2ecf20Sopenharmony_ci * PHY activity, enter lowest power SUSPEND2 mode 18238c2ecf20Sopenharmony_ci */ 18248c2ecf20Sopenharmony_ci if (!(pdata->wolopts & SUPPORTED_WAKE) || 18258c2ecf20Sopenharmony_ci !(link_up || (pdata->wolopts & WAKE_PHY))) { 18268c2ecf20Sopenharmony_ci netdev_info(dev->net, "entering SUSPEND2 mode\n"); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci /* disable energy detect (link up) & wake up events */ 18298c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 18308c2ecf20Sopenharmony_ci if (ret < 0) { 18318c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 18328c2ecf20Sopenharmony_ci goto done; 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci val &= ~(WUCSR_MPEN | WUCSR_WUEN); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 18388c2ecf20Sopenharmony_ci if (ret < 0) { 18398c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 18408c2ecf20Sopenharmony_ci goto done; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 18448c2ecf20Sopenharmony_ci if (ret < 0) { 18458c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 18468c2ecf20Sopenharmony_ci goto done; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 18528c2ecf20Sopenharmony_ci if (ret < 0) { 18538c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 18548c2ecf20Sopenharmony_ci goto done; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci ret = smsc75xx_enter_suspend2(dev); 18588c2ecf20Sopenharmony_ci goto done; 18598c2ecf20Sopenharmony_ci } 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_PHY) { 18628c2ecf20Sopenharmony_ci ret = smsc75xx_enable_phy_wakeup_interrupts(dev, 18638c2ecf20Sopenharmony_ci (PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN)); 18648c2ecf20Sopenharmony_ci if (ret < 0) { 18658c2ecf20Sopenharmony_ci netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); 18668c2ecf20Sopenharmony_ci goto done; 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* if link is down then configure EDPD and enter SUSPEND1, 18708c2ecf20Sopenharmony_ci * otherwise enter SUSPEND0 below 18718c2ecf20Sopenharmony_ci */ 18728c2ecf20Sopenharmony_ci if (!link_up) { 18738c2ecf20Sopenharmony_ci struct mii_if_info *mii = &dev->mii; 18748c2ecf20Sopenharmony_ci netdev_info(dev->net, "entering SUSPEND1 mode\n"); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci /* enable energy detect power-down mode */ 18778c2ecf20Sopenharmony_ci ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, 18788c2ecf20Sopenharmony_ci PHY_MODE_CTRL_STS); 18798c2ecf20Sopenharmony_ci if (ret < 0) { 18808c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n"); 18818c2ecf20Sopenharmony_ci goto done; 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci ret |= MODE_CTRL_STS_EDPWRDOWN; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, 18878c2ecf20Sopenharmony_ci PHY_MODE_CTRL_STS, ret); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci /* enter SUSPEND1 mode */ 18908c2ecf20Sopenharmony_ci ret = smsc75xx_enter_suspend1(dev); 18918c2ecf20Sopenharmony_ci goto done; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) { 18968c2ecf20Sopenharmony_ci int i, filter = 0; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci /* disable all filters */ 18998c2ecf20Sopenharmony_ci for (i = 0; i < WUF_NUM; i++) { 19008c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUF_CFGX + i * 4, 0); 19018c2ecf20Sopenharmony_ci if (ret < 0) { 19028c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUF_CFGX\n"); 19038c2ecf20Sopenharmony_ci goto done; 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_MCAST) { 19088c2ecf20Sopenharmony_ci const u8 mcast[] = {0x01, 0x00, 0x5E}; 19098c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling multicast detection\n"); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci val = WUF_CFGX_EN | WUF_CFGX_ATYPE_MULTICAST 19128c2ecf20Sopenharmony_ci | smsc_crc(mcast, 3); 19138c2ecf20Sopenharmony_ci ret = smsc75xx_write_wuff(dev, filter++, val, 0x0007); 19148c2ecf20Sopenharmony_ci if (ret < 0) { 19158c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing wakeup filter\n"); 19168c2ecf20Sopenharmony_ci goto done; 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci } 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_ARP) { 19218c2ecf20Sopenharmony_ci const u8 arp[] = {0x08, 0x06}; 19228c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling ARP detection\n"); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci val = WUF_CFGX_EN | WUF_CFGX_ATYPE_ALL | (0x0C << 16) 19258c2ecf20Sopenharmony_ci | smsc_crc(arp, 2); 19268c2ecf20Sopenharmony_ci ret = smsc75xx_write_wuff(dev, filter++, val, 0x0003); 19278c2ecf20Sopenharmony_ci if (ret < 0) { 19288c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing wakeup filter\n"); 19298c2ecf20Sopenharmony_ci goto done; 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* clear any pending pattern match packet status */ 19348c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 19358c2ecf20Sopenharmony_ci if (ret < 0) { 19368c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 19378c2ecf20Sopenharmony_ci goto done; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci val |= WUCSR_WUFR; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 19438c2ecf20Sopenharmony_ci if (ret < 0) { 19448c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 19458c2ecf20Sopenharmony_ci goto done; 19468c2ecf20Sopenharmony_ci } 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling packet match detection\n"); 19498c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 19508c2ecf20Sopenharmony_ci if (ret < 0) { 19518c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 19528c2ecf20Sopenharmony_ci goto done; 19538c2ecf20Sopenharmony_ci } 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci val |= WUCSR_WUEN; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 19588c2ecf20Sopenharmony_ci if (ret < 0) { 19598c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 19608c2ecf20Sopenharmony_ci goto done; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci } else { 19638c2ecf20Sopenharmony_ci netdev_info(dev->net, "disabling packet match detection\n"); 19648c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 19658c2ecf20Sopenharmony_ci if (ret < 0) { 19668c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 19678c2ecf20Sopenharmony_ci goto done; 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci val &= ~WUCSR_WUEN; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 19738c2ecf20Sopenharmony_ci if (ret < 0) { 19748c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 19758c2ecf20Sopenharmony_ci goto done; 19768c2ecf20Sopenharmony_ci } 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci /* disable magic, bcast & unicast wakeup sources */ 19808c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 19818c2ecf20Sopenharmony_ci if (ret < 0) { 19828c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 19838c2ecf20Sopenharmony_ci goto done; 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 19898c2ecf20Sopenharmony_ci if (ret < 0) { 19908c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 19918c2ecf20Sopenharmony_ci goto done; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_PHY) { 19958c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling PHY wakeup\n"); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 19988c2ecf20Sopenharmony_ci if (ret < 0) { 19998c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 20008c2ecf20Sopenharmony_ci goto done; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* clear wol status, enable energy detection */ 20048c2ecf20Sopenharmony_ci val &= ~PMT_CTL_WUPS; 20058c2ecf20Sopenharmony_ci val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 20088c2ecf20Sopenharmony_ci if (ret < 0) { 20098c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 20108c2ecf20Sopenharmony_ci goto done; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_MAGIC) { 20158c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling magic packet wakeup\n"); 20168c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 20178c2ecf20Sopenharmony_ci if (ret < 0) { 20188c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 20198c2ecf20Sopenharmony_ci goto done; 20208c2ecf20Sopenharmony_ci } 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* clear any pending magic packet status */ 20238c2ecf20Sopenharmony_ci val |= WUCSR_MPR | WUCSR_MPEN; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 20268c2ecf20Sopenharmony_ci if (ret < 0) { 20278c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 20288c2ecf20Sopenharmony_ci goto done; 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_BCAST) { 20338c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling broadcast detection\n"); 20348c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 20358c2ecf20Sopenharmony_ci if (ret < 0) { 20368c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 20378c2ecf20Sopenharmony_ci goto done; 20388c2ecf20Sopenharmony_ci } 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci val |= WUCSR_BCAST_FR | WUCSR_BCST_EN; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 20438c2ecf20Sopenharmony_ci if (ret < 0) { 20448c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 20458c2ecf20Sopenharmony_ci goto done; 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci if (pdata->wolopts & WAKE_UCAST) { 20508c2ecf20Sopenharmony_ci netdev_info(dev->net, "enabling unicast detection\n"); 20518c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 20528c2ecf20Sopenharmony_ci if (ret < 0) { 20538c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 20548c2ecf20Sopenharmony_ci goto done; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci val |= WUCSR_WUFR | WUCSR_PFDA_EN; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 20608c2ecf20Sopenharmony_ci if (ret < 0) { 20618c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 20628c2ecf20Sopenharmony_ci goto done; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci } 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci /* enable receiver to enable frame reception */ 20678c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val); 20688c2ecf20Sopenharmony_ci if (ret < 0) { 20698c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); 20708c2ecf20Sopenharmony_ci goto done; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci val |= MAC_RX_RXEN; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val); 20768c2ecf20Sopenharmony_ci if (ret < 0) { 20778c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); 20788c2ecf20Sopenharmony_ci goto done; 20798c2ecf20Sopenharmony_ci } 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci /* some wol options are enabled, so enter SUSPEND0 */ 20828c2ecf20Sopenharmony_ci netdev_info(dev->net, "entering SUSPEND0 mode\n"); 20838c2ecf20Sopenharmony_ci ret = smsc75xx_enter_suspend0(dev); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cidone: 20868c2ecf20Sopenharmony_ci /* 20878c2ecf20Sopenharmony_ci * TODO: resume() might need to handle the suspend failure 20888c2ecf20Sopenharmony_ci * in system sleep 20898c2ecf20Sopenharmony_ci */ 20908c2ecf20Sopenharmony_ci if (ret && PMSG_IS_AUTO(message)) 20918c2ecf20Sopenharmony_ci usbnet_resume(intf); 20928c2ecf20Sopenharmony_ci return ret; 20938c2ecf20Sopenharmony_ci} 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_cistatic int smsc75xx_resume(struct usb_interface *intf) 20968c2ecf20Sopenharmony_ci{ 20978c2ecf20Sopenharmony_ci struct usbnet *dev = usb_get_intfdata(intf); 20988c2ecf20Sopenharmony_ci struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 20998c2ecf20Sopenharmony_ci u8 suspend_flags = pdata->suspend_flags; 21008c2ecf20Sopenharmony_ci int ret; 21018c2ecf20Sopenharmony_ci u32 val; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci /* do this first to ensure it's cleared even in error case */ 21068c2ecf20Sopenharmony_ci pdata->suspend_flags = 0; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci if (suspend_flags & SUSPEND_ALLMODES) { 21098c2ecf20Sopenharmony_ci /* Disable wakeup sources */ 21108c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 21118c2ecf20Sopenharmony_ci if (ret < 0) { 21128c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading WUCSR\n"); 21138c2ecf20Sopenharmony_ci return ret; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN 21178c2ecf20Sopenharmony_ci | WUCSR_BCST_EN); 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); 21208c2ecf20Sopenharmony_ci if (ret < 0) { 21218c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing WUCSR\n"); 21228c2ecf20Sopenharmony_ci return ret; 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci /* clear wake-up status */ 21268c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 21278c2ecf20Sopenharmony_ci if (ret < 0) { 21288c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 21298c2ecf20Sopenharmony_ci return ret; 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci val &= ~PMT_CTL_WOL_EN; 21338c2ecf20Sopenharmony_ci val |= PMT_CTL_WUPS; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 21368c2ecf20Sopenharmony_ci if (ret < 0) { 21378c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 21388c2ecf20Sopenharmony_ci return ret; 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci } 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci if (suspend_flags & SUSPEND_SUSPEND2) { 21438c2ecf20Sopenharmony_ci netdev_info(dev->net, "resuming from SUSPEND2\n"); 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 21468c2ecf20Sopenharmony_ci if (ret < 0) { 21478c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error reading PMT_CTL\n"); 21488c2ecf20Sopenharmony_ci return ret; 21498c2ecf20Sopenharmony_ci } 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci val |= PMT_CTL_PHY_PWRUP; 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 21548c2ecf20Sopenharmony_ci if (ret < 0) { 21558c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error writing PMT_CTL\n"); 21568c2ecf20Sopenharmony_ci return ret; 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci ret = smsc75xx_wait_ready(dev, 1); 21618c2ecf20Sopenharmony_ci if (ret < 0) { 21628c2ecf20Sopenharmony_ci netdev_warn(dev->net, "device not ready in smsc75xx_resume\n"); 21638c2ecf20Sopenharmony_ci return ret; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci return usbnet_resume(intf); 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_cistatic void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb, 21708c2ecf20Sopenharmony_ci u32 rx_cmd_a, u32 rx_cmd_b) 21718c2ecf20Sopenharmony_ci{ 21728c2ecf20Sopenharmony_ci if (!(dev->net->features & NETIF_F_RXCSUM) || 21738c2ecf20Sopenharmony_ci unlikely(rx_cmd_a & RX_CMD_A_LCSM)) { 21748c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 21758c2ecf20Sopenharmony_ci } else { 21768c2ecf20Sopenharmony_ci skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT)); 21778c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci} 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_cistatic int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 21828c2ecf20Sopenharmony_ci{ 21838c2ecf20Sopenharmony_ci /* This check is no longer done by usbnet */ 21848c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 21858c2ecf20Sopenharmony_ci return 0; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci while (skb->len > 0) { 21888c2ecf20Sopenharmony_ci u32 rx_cmd_a, rx_cmd_b, align_count, size; 21898c2ecf20Sopenharmony_ci struct sk_buff *ax_skb; 21908c2ecf20Sopenharmony_ci unsigned char *packet; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci rx_cmd_a = get_unaligned_le32(skb->data); 21938c2ecf20Sopenharmony_ci skb_pull(skb, 4); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci rx_cmd_b = get_unaligned_le32(skb->data); 21968c2ecf20Sopenharmony_ci skb_pull(skb, 4 + RXW_PADDING); 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci packet = skb->data; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci /* get the packet length */ 22018c2ecf20Sopenharmony_ci size = (rx_cmd_a & RX_CMD_A_LEN) - RXW_PADDING; 22028c2ecf20Sopenharmony_ci align_count = (4 - ((size + RXW_PADDING) % 4)) % 4; 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci if (unlikely(size > skb->len)) { 22058c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 22068c2ecf20Sopenharmony_ci "size err rx_cmd_a=0x%08x\n", 22078c2ecf20Sopenharmony_ci rx_cmd_a); 22088c2ecf20Sopenharmony_ci return 0; 22098c2ecf20Sopenharmony_ci } 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci if (unlikely(rx_cmd_a & RX_CMD_A_RED)) { 22128c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 22138c2ecf20Sopenharmony_ci "Error rx_cmd_a=0x%08x\n", rx_cmd_a); 22148c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 22158c2ecf20Sopenharmony_ci dev->net->stats.rx_dropped++; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (rx_cmd_a & RX_CMD_A_FCS) 22188c2ecf20Sopenharmony_ci dev->net->stats.rx_crc_errors++; 22198c2ecf20Sopenharmony_ci else if (rx_cmd_a & (RX_CMD_A_LONG | RX_CMD_A_RUNT)) 22208c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 22218c2ecf20Sopenharmony_ci } else { 22228c2ecf20Sopenharmony_ci /* MAX_SINGLE_PACKET_SIZE + 4(CRC) + 2(COE) + 4(Vlan) */ 22238c2ecf20Sopenharmony_ci if (unlikely(size > (MAX_SINGLE_PACKET_SIZE + ETH_HLEN + 12))) { 22248c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 22258c2ecf20Sopenharmony_ci "size err rx_cmd_a=0x%08x\n", 22268c2ecf20Sopenharmony_ci rx_cmd_a); 22278c2ecf20Sopenharmony_ci return 0; 22288c2ecf20Sopenharmony_ci } 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci /* last frame in this batch */ 22318c2ecf20Sopenharmony_ci if (skb->len == size) { 22328c2ecf20Sopenharmony_ci smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a, 22338c2ecf20Sopenharmony_ci rx_cmd_b); 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - 4); /* remove fcs */ 22368c2ecf20Sopenharmony_ci skb->truesize = size + sizeof(struct sk_buff); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci return 1; 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci ax_skb = skb_clone(skb, GFP_ATOMIC); 22428c2ecf20Sopenharmony_ci if (unlikely(!ax_skb)) { 22438c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error allocating skb\n"); 22448c2ecf20Sopenharmony_ci return 0; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci ax_skb->len = size; 22488c2ecf20Sopenharmony_ci ax_skb->data = packet; 22498c2ecf20Sopenharmony_ci skb_set_tail_pointer(ax_skb, size); 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a, 22528c2ecf20Sopenharmony_ci rx_cmd_b); 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ 22558c2ecf20Sopenharmony_ci ax_skb->truesize = size + sizeof(struct sk_buff); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci usbnet_skb_return(dev, ax_skb); 22588c2ecf20Sopenharmony_ci } 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci skb_pull(skb, size); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci /* padding bytes before the next frame starts */ 22638c2ecf20Sopenharmony_ci if (skb->len) 22648c2ecf20Sopenharmony_ci skb_pull(skb, align_count); 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci return 1; 22688c2ecf20Sopenharmony_ci} 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_cistatic struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev, 22718c2ecf20Sopenharmony_ci struct sk_buff *skb, gfp_t flags) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci u32 tx_cmd_a, tx_cmd_b; 22748c2ecf20Sopenharmony_ci void *ptr; 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (skb_cow_head(skb, SMSC75XX_TX_OVERHEAD)) { 22778c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 22788c2ecf20Sopenharmony_ci return NULL; 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 22848c2ecf20Sopenharmony_ci tx_cmd_a |= TX_CMD_A_IPE | TX_CMD_A_TPE; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 22878c2ecf20Sopenharmony_ci u16 mss = max(skb_shinfo(skb)->gso_size, TX_MSS_MIN); 22888c2ecf20Sopenharmony_ci tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT) & TX_CMD_B_MSS; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci tx_cmd_a |= TX_CMD_A_LSO; 22918c2ecf20Sopenharmony_ci } else { 22928c2ecf20Sopenharmony_ci tx_cmd_b = 0; 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci ptr = skb_push(skb, 8); 22968c2ecf20Sopenharmony_ci put_unaligned_le32(tx_cmd_a, ptr); 22978c2ecf20Sopenharmony_ci put_unaligned_le32(tx_cmd_b, ptr + 4); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci return skb; 23008c2ecf20Sopenharmony_ci} 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_cistatic int smsc75xx_manage_power(struct usbnet *dev, int on) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci dev->intf->needs_remote_wakeup = on; 23058c2ecf20Sopenharmony_ci return 0; 23068c2ecf20Sopenharmony_ci} 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_cistatic const struct driver_info smsc75xx_info = { 23098c2ecf20Sopenharmony_ci .description = "smsc75xx USB 2.0 Gigabit Ethernet", 23108c2ecf20Sopenharmony_ci .bind = smsc75xx_bind, 23118c2ecf20Sopenharmony_ci .unbind = smsc75xx_unbind, 23128c2ecf20Sopenharmony_ci .link_reset = smsc75xx_link_reset, 23138c2ecf20Sopenharmony_ci .reset = smsc75xx_reset, 23148c2ecf20Sopenharmony_ci .rx_fixup = smsc75xx_rx_fixup, 23158c2ecf20Sopenharmony_ci .tx_fixup = smsc75xx_tx_fixup, 23168c2ecf20Sopenharmony_ci .status = smsc75xx_status, 23178c2ecf20Sopenharmony_ci .manage_power = smsc75xx_manage_power, 23188c2ecf20Sopenharmony_ci .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, 23198c2ecf20Sopenharmony_ci}; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_cistatic const struct usb_device_id products[] = { 23228c2ecf20Sopenharmony_ci { 23238c2ecf20Sopenharmony_ci /* SMSC7500 USB Gigabit Ethernet Device */ 23248c2ecf20Sopenharmony_ci USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7500), 23258c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &smsc75xx_info, 23268c2ecf20Sopenharmony_ci }, 23278c2ecf20Sopenharmony_ci { 23288c2ecf20Sopenharmony_ci /* SMSC7500 USB Gigabit Ethernet Device */ 23298c2ecf20Sopenharmony_ci USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7505), 23308c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &smsc75xx_info, 23318c2ecf20Sopenharmony_ci }, 23328c2ecf20Sopenharmony_ci { }, /* END */ 23338c2ecf20Sopenharmony_ci}; 23348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_cistatic struct usb_driver smsc75xx_driver = { 23378c2ecf20Sopenharmony_ci .name = SMSC_CHIPNAME, 23388c2ecf20Sopenharmony_ci .id_table = products, 23398c2ecf20Sopenharmony_ci .probe = usbnet_probe, 23408c2ecf20Sopenharmony_ci .suspend = smsc75xx_suspend, 23418c2ecf20Sopenharmony_ci .resume = smsc75xx_resume, 23428c2ecf20Sopenharmony_ci .reset_resume = smsc75xx_resume, 23438c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 23448c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 23458c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 23468c2ecf20Sopenharmony_ci}; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_cimodule_usb_driver(smsc75xx_driver); 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nancy Lin"); 23518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>"); 23528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMSC75XX USB 2.0 Gigabit Ethernet Devices"); 23538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2354