18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Microchip Technology 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/version.h> 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 98c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 108c2ecf20Sopenharmony_ci#include <linux/usb.h> 118c2ecf20Sopenharmony_ci#include <linux/crc32.h> 128c2ecf20Sopenharmony_ci#include <linux/signal.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/linkmode.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/ip.h> 198c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 208c2ecf20Sopenharmony_ci#include <linux/mdio.h> 218c2ecf20Sopenharmony_ci#include <linux/phy.h> 228c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 238c2ecf20Sopenharmony_ci#include <net/vxlan.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 268c2ecf20Sopenharmony_ci#include <linux/irq.h> 278c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 288c2ecf20Sopenharmony_ci#include <linux/microchipphy.h> 298c2ecf20Sopenharmony_ci#include <linux/phy_fixed.h> 308c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 318c2ecf20Sopenharmony_ci#include <linux/of_net.h> 328c2ecf20Sopenharmony_ci#include "lan78xx.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>" 358c2ecf20Sopenharmony_ci#define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices" 368c2ecf20Sopenharmony_ci#define DRIVER_NAME "lan78xx" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TX_TIMEOUT_JIFFIES (5 * HZ) 398c2ecf20Sopenharmony_ci#define THROTTLE_JIFFIES (HZ / 8) 408c2ecf20Sopenharmony_ci#define UNLINK_TIMEOUT_MS 3 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define RX_MAX_QUEUE_MEMORY (60 * 1518) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define SS_USB_PKT_SIZE (1024) 458c2ecf20Sopenharmony_ci#define HS_USB_PKT_SIZE (512) 468c2ecf20Sopenharmony_ci#define FS_USB_PKT_SIZE (64) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define MAX_RX_FIFO_SIZE (12 * 1024) 498c2ecf20Sopenharmony_ci#define MAX_TX_FIFO_SIZE (12 * 1024) 508c2ecf20Sopenharmony_ci#define DEFAULT_BURST_CAP_SIZE (MAX_TX_FIFO_SIZE) 518c2ecf20Sopenharmony_ci#define DEFAULT_BULK_IN_DELAY (0x0800) 528c2ecf20Sopenharmony_ci#define MAX_SINGLE_PACKET_SIZE (9000) 538c2ecf20Sopenharmony_ci#define DEFAULT_TX_CSUM_ENABLE (true) 548c2ecf20Sopenharmony_ci#define DEFAULT_RX_CSUM_ENABLE (true) 558c2ecf20Sopenharmony_ci#define DEFAULT_TSO_CSUM_ENABLE (true) 568c2ecf20Sopenharmony_ci#define DEFAULT_VLAN_FILTER_ENABLE (true) 578c2ecf20Sopenharmony_ci#define DEFAULT_VLAN_RX_OFFLOAD (true) 588c2ecf20Sopenharmony_ci#define TX_OVERHEAD (8) 598c2ecf20Sopenharmony_ci#define RXW_PADDING 2 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define LAN78XX_USB_VENDOR_ID (0x0424) 628c2ecf20Sopenharmony_ci#define LAN7800_USB_PRODUCT_ID (0x7800) 638c2ecf20Sopenharmony_ci#define LAN7850_USB_PRODUCT_ID (0x7850) 648c2ecf20Sopenharmony_ci#define LAN7801_USB_PRODUCT_ID (0x7801) 658c2ecf20Sopenharmony_ci#define LAN78XX_EEPROM_MAGIC (0x78A5) 668c2ecf20Sopenharmony_ci#define LAN78XX_OTP_MAGIC (0x78F3) 678c2ecf20Sopenharmony_ci#define AT29M2AF_USB_VENDOR_ID (0x07C9) 688c2ecf20Sopenharmony_ci#define AT29M2AF_USB_PRODUCT_ID (0x0012) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define MII_READ 1 718c2ecf20Sopenharmony_ci#define MII_WRITE 0 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define EEPROM_INDICATOR (0xA5) 748c2ecf20Sopenharmony_ci#define EEPROM_MAC_OFFSET (0x01) 758c2ecf20Sopenharmony_ci#define MAX_EEPROM_SIZE 512 768c2ecf20Sopenharmony_ci#define OTP_INDICATOR_1 (0xF3) 778c2ecf20Sopenharmony_ci#define OTP_INDICATOR_2 (0xF7) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define WAKE_ALL (WAKE_PHY | WAKE_UCAST | \ 808c2ecf20Sopenharmony_ci WAKE_MCAST | WAKE_BCAST | \ 818c2ecf20Sopenharmony_ci WAKE_ARP | WAKE_MAGIC) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* USB related defines */ 848c2ecf20Sopenharmony_ci#define BULK_IN_PIPE 1 858c2ecf20Sopenharmony_ci#define BULK_OUT_PIPE 2 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* default autosuspend delay (mSec)*/ 888c2ecf20Sopenharmony_ci#define DEFAULT_AUTOSUSPEND_DELAY (10 * 1000) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* statistic update interval (mSec) */ 918c2ecf20Sopenharmony_ci#define STAT_UPDATE_TIMER (1 * 1000) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* defines interrupts from interrupt EP */ 948c2ecf20Sopenharmony_ci#define MAX_INT_EP (32) 958c2ecf20Sopenharmony_ci#define INT_EP_INTEP (31) 968c2ecf20Sopenharmony_ci#define INT_EP_OTP_WR_DONE (28) 978c2ecf20Sopenharmony_ci#define INT_EP_EEE_TX_LPI_START (26) 988c2ecf20Sopenharmony_ci#define INT_EP_EEE_TX_LPI_STOP (25) 998c2ecf20Sopenharmony_ci#define INT_EP_EEE_RX_LPI (24) 1008c2ecf20Sopenharmony_ci#define INT_EP_MAC_RESET_TIMEOUT (23) 1018c2ecf20Sopenharmony_ci#define INT_EP_RDFO (22) 1028c2ecf20Sopenharmony_ci#define INT_EP_TXE (21) 1038c2ecf20Sopenharmony_ci#define INT_EP_USB_STATUS (20) 1048c2ecf20Sopenharmony_ci#define INT_EP_TX_DIS (19) 1058c2ecf20Sopenharmony_ci#define INT_EP_RX_DIS (18) 1068c2ecf20Sopenharmony_ci#define INT_EP_PHY (17) 1078c2ecf20Sopenharmony_ci#define INT_EP_DP (16) 1088c2ecf20Sopenharmony_ci#define INT_EP_MAC_ERR (15) 1098c2ecf20Sopenharmony_ci#define INT_EP_TDFU (14) 1108c2ecf20Sopenharmony_ci#define INT_EP_TDFO (13) 1118c2ecf20Sopenharmony_ci#define INT_EP_UTX (12) 1128c2ecf20Sopenharmony_ci#define INT_EP_GPIO_11 (11) 1138c2ecf20Sopenharmony_ci#define INT_EP_GPIO_10 (10) 1148c2ecf20Sopenharmony_ci#define INT_EP_GPIO_9 (9) 1158c2ecf20Sopenharmony_ci#define INT_EP_GPIO_8 (8) 1168c2ecf20Sopenharmony_ci#define INT_EP_GPIO_7 (7) 1178c2ecf20Sopenharmony_ci#define INT_EP_GPIO_6 (6) 1188c2ecf20Sopenharmony_ci#define INT_EP_GPIO_5 (5) 1198c2ecf20Sopenharmony_ci#define INT_EP_GPIO_4 (4) 1208c2ecf20Sopenharmony_ci#define INT_EP_GPIO_3 (3) 1218c2ecf20Sopenharmony_ci#define INT_EP_GPIO_2 (2) 1228c2ecf20Sopenharmony_ci#define INT_EP_GPIO_1 (1) 1238c2ecf20Sopenharmony_ci#define INT_EP_GPIO_0 (0) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic const char lan78xx_gstrings[][ETH_GSTRING_LEN] = { 1268c2ecf20Sopenharmony_ci "RX FCS Errors", 1278c2ecf20Sopenharmony_ci "RX Alignment Errors", 1288c2ecf20Sopenharmony_ci "Rx Fragment Errors", 1298c2ecf20Sopenharmony_ci "RX Jabber Errors", 1308c2ecf20Sopenharmony_ci "RX Undersize Frame Errors", 1318c2ecf20Sopenharmony_ci "RX Oversize Frame Errors", 1328c2ecf20Sopenharmony_ci "RX Dropped Frames", 1338c2ecf20Sopenharmony_ci "RX Unicast Byte Count", 1348c2ecf20Sopenharmony_ci "RX Broadcast Byte Count", 1358c2ecf20Sopenharmony_ci "RX Multicast Byte Count", 1368c2ecf20Sopenharmony_ci "RX Unicast Frames", 1378c2ecf20Sopenharmony_ci "RX Broadcast Frames", 1388c2ecf20Sopenharmony_ci "RX Multicast Frames", 1398c2ecf20Sopenharmony_ci "RX Pause Frames", 1408c2ecf20Sopenharmony_ci "RX 64 Byte Frames", 1418c2ecf20Sopenharmony_ci "RX 65 - 127 Byte Frames", 1428c2ecf20Sopenharmony_ci "RX 128 - 255 Byte Frames", 1438c2ecf20Sopenharmony_ci "RX 256 - 511 Bytes Frames", 1448c2ecf20Sopenharmony_ci "RX 512 - 1023 Byte Frames", 1458c2ecf20Sopenharmony_ci "RX 1024 - 1518 Byte Frames", 1468c2ecf20Sopenharmony_ci "RX Greater 1518 Byte Frames", 1478c2ecf20Sopenharmony_ci "EEE RX LPI Transitions", 1488c2ecf20Sopenharmony_ci "EEE RX LPI Time", 1498c2ecf20Sopenharmony_ci "TX FCS Errors", 1508c2ecf20Sopenharmony_ci "TX Excess Deferral Errors", 1518c2ecf20Sopenharmony_ci "TX Carrier Errors", 1528c2ecf20Sopenharmony_ci "TX Bad Byte Count", 1538c2ecf20Sopenharmony_ci "TX Single Collisions", 1548c2ecf20Sopenharmony_ci "TX Multiple Collisions", 1558c2ecf20Sopenharmony_ci "TX Excessive Collision", 1568c2ecf20Sopenharmony_ci "TX Late Collisions", 1578c2ecf20Sopenharmony_ci "TX Unicast Byte Count", 1588c2ecf20Sopenharmony_ci "TX Broadcast Byte Count", 1598c2ecf20Sopenharmony_ci "TX Multicast Byte Count", 1608c2ecf20Sopenharmony_ci "TX Unicast Frames", 1618c2ecf20Sopenharmony_ci "TX Broadcast Frames", 1628c2ecf20Sopenharmony_ci "TX Multicast Frames", 1638c2ecf20Sopenharmony_ci "TX Pause Frames", 1648c2ecf20Sopenharmony_ci "TX 64 Byte Frames", 1658c2ecf20Sopenharmony_ci "TX 65 - 127 Byte Frames", 1668c2ecf20Sopenharmony_ci "TX 128 - 255 Byte Frames", 1678c2ecf20Sopenharmony_ci "TX 256 - 511 Bytes Frames", 1688c2ecf20Sopenharmony_ci "TX 512 - 1023 Byte Frames", 1698c2ecf20Sopenharmony_ci "TX 1024 - 1518 Byte Frames", 1708c2ecf20Sopenharmony_ci "TX Greater 1518 Byte Frames", 1718c2ecf20Sopenharmony_ci "EEE TX LPI Transitions", 1728c2ecf20Sopenharmony_ci "EEE TX LPI Time", 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistruct lan78xx_statstage { 1768c2ecf20Sopenharmony_ci u32 rx_fcs_errors; 1778c2ecf20Sopenharmony_ci u32 rx_alignment_errors; 1788c2ecf20Sopenharmony_ci u32 rx_fragment_errors; 1798c2ecf20Sopenharmony_ci u32 rx_jabber_errors; 1808c2ecf20Sopenharmony_ci u32 rx_undersize_frame_errors; 1818c2ecf20Sopenharmony_ci u32 rx_oversize_frame_errors; 1828c2ecf20Sopenharmony_ci u32 rx_dropped_frames; 1838c2ecf20Sopenharmony_ci u32 rx_unicast_byte_count; 1848c2ecf20Sopenharmony_ci u32 rx_broadcast_byte_count; 1858c2ecf20Sopenharmony_ci u32 rx_multicast_byte_count; 1868c2ecf20Sopenharmony_ci u32 rx_unicast_frames; 1878c2ecf20Sopenharmony_ci u32 rx_broadcast_frames; 1888c2ecf20Sopenharmony_ci u32 rx_multicast_frames; 1898c2ecf20Sopenharmony_ci u32 rx_pause_frames; 1908c2ecf20Sopenharmony_ci u32 rx_64_byte_frames; 1918c2ecf20Sopenharmony_ci u32 rx_65_127_byte_frames; 1928c2ecf20Sopenharmony_ci u32 rx_128_255_byte_frames; 1938c2ecf20Sopenharmony_ci u32 rx_256_511_bytes_frames; 1948c2ecf20Sopenharmony_ci u32 rx_512_1023_byte_frames; 1958c2ecf20Sopenharmony_ci u32 rx_1024_1518_byte_frames; 1968c2ecf20Sopenharmony_ci u32 rx_greater_1518_byte_frames; 1978c2ecf20Sopenharmony_ci u32 eee_rx_lpi_transitions; 1988c2ecf20Sopenharmony_ci u32 eee_rx_lpi_time; 1998c2ecf20Sopenharmony_ci u32 tx_fcs_errors; 2008c2ecf20Sopenharmony_ci u32 tx_excess_deferral_errors; 2018c2ecf20Sopenharmony_ci u32 tx_carrier_errors; 2028c2ecf20Sopenharmony_ci u32 tx_bad_byte_count; 2038c2ecf20Sopenharmony_ci u32 tx_single_collisions; 2048c2ecf20Sopenharmony_ci u32 tx_multiple_collisions; 2058c2ecf20Sopenharmony_ci u32 tx_excessive_collision; 2068c2ecf20Sopenharmony_ci u32 tx_late_collisions; 2078c2ecf20Sopenharmony_ci u32 tx_unicast_byte_count; 2088c2ecf20Sopenharmony_ci u32 tx_broadcast_byte_count; 2098c2ecf20Sopenharmony_ci u32 tx_multicast_byte_count; 2108c2ecf20Sopenharmony_ci u32 tx_unicast_frames; 2118c2ecf20Sopenharmony_ci u32 tx_broadcast_frames; 2128c2ecf20Sopenharmony_ci u32 tx_multicast_frames; 2138c2ecf20Sopenharmony_ci u32 tx_pause_frames; 2148c2ecf20Sopenharmony_ci u32 tx_64_byte_frames; 2158c2ecf20Sopenharmony_ci u32 tx_65_127_byte_frames; 2168c2ecf20Sopenharmony_ci u32 tx_128_255_byte_frames; 2178c2ecf20Sopenharmony_ci u32 tx_256_511_bytes_frames; 2188c2ecf20Sopenharmony_ci u32 tx_512_1023_byte_frames; 2198c2ecf20Sopenharmony_ci u32 tx_1024_1518_byte_frames; 2208c2ecf20Sopenharmony_ci u32 tx_greater_1518_byte_frames; 2218c2ecf20Sopenharmony_ci u32 eee_tx_lpi_transitions; 2228c2ecf20Sopenharmony_ci u32 eee_tx_lpi_time; 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistruct lan78xx_statstage64 { 2268c2ecf20Sopenharmony_ci u64 rx_fcs_errors; 2278c2ecf20Sopenharmony_ci u64 rx_alignment_errors; 2288c2ecf20Sopenharmony_ci u64 rx_fragment_errors; 2298c2ecf20Sopenharmony_ci u64 rx_jabber_errors; 2308c2ecf20Sopenharmony_ci u64 rx_undersize_frame_errors; 2318c2ecf20Sopenharmony_ci u64 rx_oversize_frame_errors; 2328c2ecf20Sopenharmony_ci u64 rx_dropped_frames; 2338c2ecf20Sopenharmony_ci u64 rx_unicast_byte_count; 2348c2ecf20Sopenharmony_ci u64 rx_broadcast_byte_count; 2358c2ecf20Sopenharmony_ci u64 rx_multicast_byte_count; 2368c2ecf20Sopenharmony_ci u64 rx_unicast_frames; 2378c2ecf20Sopenharmony_ci u64 rx_broadcast_frames; 2388c2ecf20Sopenharmony_ci u64 rx_multicast_frames; 2398c2ecf20Sopenharmony_ci u64 rx_pause_frames; 2408c2ecf20Sopenharmony_ci u64 rx_64_byte_frames; 2418c2ecf20Sopenharmony_ci u64 rx_65_127_byte_frames; 2428c2ecf20Sopenharmony_ci u64 rx_128_255_byte_frames; 2438c2ecf20Sopenharmony_ci u64 rx_256_511_bytes_frames; 2448c2ecf20Sopenharmony_ci u64 rx_512_1023_byte_frames; 2458c2ecf20Sopenharmony_ci u64 rx_1024_1518_byte_frames; 2468c2ecf20Sopenharmony_ci u64 rx_greater_1518_byte_frames; 2478c2ecf20Sopenharmony_ci u64 eee_rx_lpi_transitions; 2488c2ecf20Sopenharmony_ci u64 eee_rx_lpi_time; 2498c2ecf20Sopenharmony_ci u64 tx_fcs_errors; 2508c2ecf20Sopenharmony_ci u64 tx_excess_deferral_errors; 2518c2ecf20Sopenharmony_ci u64 tx_carrier_errors; 2528c2ecf20Sopenharmony_ci u64 tx_bad_byte_count; 2538c2ecf20Sopenharmony_ci u64 tx_single_collisions; 2548c2ecf20Sopenharmony_ci u64 tx_multiple_collisions; 2558c2ecf20Sopenharmony_ci u64 tx_excessive_collision; 2568c2ecf20Sopenharmony_ci u64 tx_late_collisions; 2578c2ecf20Sopenharmony_ci u64 tx_unicast_byte_count; 2588c2ecf20Sopenharmony_ci u64 tx_broadcast_byte_count; 2598c2ecf20Sopenharmony_ci u64 tx_multicast_byte_count; 2608c2ecf20Sopenharmony_ci u64 tx_unicast_frames; 2618c2ecf20Sopenharmony_ci u64 tx_broadcast_frames; 2628c2ecf20Sopenharmony_ci u64 tx_multicast_frames; 2638c2ecf20Sopenharmony_ci u64 tx_pause_frames; 2648c2ecf20Sopenharmony_ci u64 tx_64_byte_frames; 2658c2ecf20Sopenharmony_ci u64 tx_65_127_byte_frames; 2668c2ecf20Sopenharmony_ci u64 tx_128_255_byte_frames; 2678c2ecf20Sopenharmony_ci u64 tx_256_511_bytes_frames; 2688c2ecf20Sopenharmony_ci u64 tx_512_1023_byte_frames; 2698c2ecf20Sopenharmony_ci u64 tx_1024_1518_byte_frames; 2708c2ecf20Sopenharmony_ci u64 tx_greater_1518_byte_frames; 2718c2ecf20Sopenharmony_ci u64 eee_tx_lpi_transitions; 2728c2ecf20Sopenharmony_ci u64 eee_tx_lpi_time; 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic u32 lan78xx_regs[] = { 2768c2ecf20Sopenharmony_ci ID_REV, 2778c2ecf20Sopenharmony_ci INT_STS, 2788c2ecf20Sopenharmony_ci HW_CFG, 2798c2ecf20Sopenharmony_ci PMT_CTL, 2808c2ecf20Sopenharmony_ci E2P_CMD, 2818c2ecf20Sopenharmony_ci E2P_DATA, 2828c2ecf20Sopenharmony_ci USB_STATUS, 2838c2ecf20Sopenharmony_ci VLAN_TYPE, 2848c2ecf20Sopenharmony_ci MAC_CR, 2858c2ecf20Sopenharmony_ci MAC_RX, 2868c2ecf20Sopenharmony_ci MAC_TX, 2878c2ecf20Sopenharmony_ci FLOW, 2888c2ecf20Sopenharmony_ci ERR_STS, 2898c2ecf20Sopenharmony_ci MII_ACC, 2908c2ecf20Sopenharmony_ci MII_DATA, 2918c2ecf20Sopenharmony_ci EEE_TX_LPI_REQ_DLY, 2928c2ecf20Sopenharmony_ci EEE_TW_TX_SYS, 2938c2ecf20Sopenharmony_ci EEE_TX_LPI_REM_DLY, 2948c2ecf20Sopenharmony_ci WUCSR 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#define PHY_REG_SIZE (32 * sizeof(u32)) 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistruct lan78xx_net; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistruct lan78xx_priv { 3028c2ecf20Sopenharmony_ci struct lan78xx_net *dev; 3038c2ecf20Sopenharmony_ci u32 rfe_ctl; 3048c2ecf20Sopenharmony_ci u32 mchash_table[DP_SEL_VHF_HASH_LEN]; /* multicat hash table */ 3058c2ecf20Sopenharmony_ci u32 pfilter_table[NUM_OF_MAF][2]; /* perfect filter table */ 3068c2ecf20Sopenharmony_ci u32 vlan_table[DP_SEL_VHF_VLAN_LEN]; 3078c2ecf20Sopenharmony_ci struct mutex dataport_mutex; /* for dataport access */ 3088c2ecf20Sopenharmony_ci spinlock_t rfe_ctl_lock; /* for rfe register access */ 3098c2ecf20Sopenharmony_ci struct work_struct set_multicast; 3108c2ecf20Sopenharmony_ci struct work_struct set_vlan; 3118c2ecf20Sopenharmony_ci u32 wol; 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cienum skb_state { 3158c2ecf20Sopenharmony_ci illegal = 0, 3168c2ecf20Sopenharmony_ci tx_start, 3178c2ecf20Sopenharmony_ci tx_done, 3188c2ecf20Sopenharmony_ci rx_start, 3198c2ecf20Sopenharmony_ci rx_done, 3208c2ecf20Sopenharmony_ci rx_cleanup, 3218c2ecf20Sopenharmony_ci unlink_start 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistruct skb_data { /* skb->cb is one of these */ 3258c2ecf20Sopenharmony_ci struct urb *urb; 3268c2ecf20Sopenharmony_ci struct lan78xx_net *dev; 3278c2ecf20Sopenharmony_ci enum skb_state state; 3288c2ecf20Sopenharmony_ci size_t length; 3298c2ecf20Sopenharmony_ci int num_of_packet; 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistruct usb_context { 3338c2ecf20Sopenharmony_ci struct usb_ctrlrequest req; 3348c2ecf20Sopenharmony_ci struct lan78xx_net *dev; 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#define EVENT_TX_HALT 0 3388c2ecf20Sopenharmony_ci#define EVENT_RX_HALT 1 3398c2ecf20Sopenharmony_ci#define EVENT_RX_MEMORY 2 3408c2ecf20Sopenharmony_ci#define EVENT_STS_SPLIT 3 3418c2ecf20Sopenharmony_ci#define EVENT_LINK_RESET 4 3428c2ecf20Sopenharmony_ci#define EVENT_RX_PAUSED 5 3438c2ecf20Sopenharmony_ci#define EVENT_DEV_WAKING 6 3448c2ecf20Sopenharmony_ci#define EVENT_DEV_ASLEEP 7 3458c2ecf20Sopenharmony_ci#define EVENT_DEV_OPEN 8 3468c2ecf20Sopenharmony_ci#define EVENT_STAT_UPDATE 9 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistruct statstage { 3498c2ecf20Sopenharmony_ci struct mutex access_lock; /* for stats access */ 3508c2ecf20Sopenharmony_ci struct lan78xx_statstage saved; 3518c2ecf20Sopenharmony_ci struct lan78xx_statstage rollover_count; 3528c2ecf20Sopenharmony_ci struct lan78xx_statstage rollover_max; 3538c2ecf20Sopenharmony_ci struct lan78xx_statstage64 curr_stat; 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistruct irq_domain_data { 3578c2ecf20Sopenharmony_ci struct irq_domain *irqdomain; 3588c2ecf20Sopenharmony_ci unsigned int phyirq; 3598c2ecf20Sopenharmony_ci struct irq_chip *irqchip; 3608c2ecf20Sopenharmony_ci irq_flow_handler_t irq_handler; 3618c2ecf20Sopenharmony_ci u32 irqenable; 3628c2ecf20Sopenharmony_ci struct mutex irq_lock; /* for irq bus access */ 3638c2ecf20Sopenharmony_ci}; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistruct lan78xx_net { 3668c2ecf20Sopenharmony_ci struct net_device *net; 3678c2ecf20Sopenharmony_ci struct usb_device *udev; 3688c2ecf20Sopenharmony_ci struct usb_interface *intf; 3698c2ecf20Sopenharmony_ci void *driver_priv; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci int rx_qlen; 3728c2ecf20Sopenharmony_ci int tx_qlen; 3738c2ecf20Sopenharmony_ci struct sk_buff_head rxq; 3748c2ecf20Sopenharmony_ci struct sk_buff_head txq; 3758c2ecf20Sopenharmony_ci struct sk_buff_head done; 3768c2ecf20Sopenharmony_ci struct sk_buff_head rxq_pause; 3778c2ecf20Sopenharmony_ci struct sk_buff_head txq_pend; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci struct tasklet_struct bh; 3808c2ecf20Sopenharmony_ci struct delayed_work wq; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci int msg_enable; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci struct urb *urb_intr; 3858c2ecf20Sopenharmony_ci struct usb_anchor deferred; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci struct mutex phy_mutex; /* for phy access */ 3888c2ecf20Sopenharmony_ci unsigned pipe_in, pipe_out, pipe_intr; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci u32 hard_mtu; /* count any extra framing */ 3918c2ecf20Sopenharmony_ci size_t rx_urb_size; /* size for rx urbs */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci unsigned long flags; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci wait_queue_head_t *wait; 3968c2ecf20Sopenharmony_ci unsigned char suspend_count; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci unsigned maxpacket; 3998c2ecf20Sopenharmony_ci struct timer_list delay; 4008c2ecf20Sopenharmony_ci struct timer_list stat_monitor; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci unsigned long data[5]; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci int link_on; 4058c2ecf20Sopenharmony_ci u8 mdix_ctrl; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci u32 chipid; 4088c2ecf20Sopenharmony_ci u32 chiprev; 4098c2ecf20Sopenharmony_ci struct mii_bus *mdiobus; 4108c2ecf20Sopenharmony_ci phy_interface_t interface; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci int fc_autoneg; 4138c2ecf20Sopenharmony_ci u8 fc_request_control; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci int delta; 4168c2ecf20Sopenharmony_ci struct statstage stats; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci struct irq_domain_data domain_data; 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* define external phy id */ 4228c2ecf20Sopenharmony_ci#define PHY_LAN8835 (0x0007C130) 4238c2ecf20Sopenharmony_ci#define PHY_KSZ9031RNX (0x00221620) 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* use ethtool to change the level for any given device */ 4268c2ecf20Sopenharmony_cistatic int msg_level = -1; 4278c2ecf20Sopenharmony_cimodule_param(msg_level, int, 0); 4288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(msg_level, "Override default message level"); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL); 4338c2ecf20Sopenharmony_ci int ret; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (!buf) 4368c2ecf20Sopenharmony_ci return -ENOMEM; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), 4398c2ecf20Sopenharmony_ci USB_VENDOR_REQUEST_READ_REGISTER, 4408c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4418c2ecf20Sopenharmony_ci 0, index, buf, 4, USB_CTRL_GET_TIMEOUT); 4428c2ecf20Sopenharmony_ci if (likely(ret >= 0)) { 4438c2ecf20Sopenharmony_ci le32_to_cpus(buf); 4448c2ecf20Sopenharmony_ci *data = *buf; 4458c2ecf20Sopenharmony_ci } else { 4468c2ecf20Sopenharmony_ci netdev_warn(dev->net, 4478c2ecf20Sopenharmony_ci "Failed to read register index 0x%08x. ret = %d", 4488c2ecf20Sopenharmony_ci index, ret); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci kfree(buf); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int lan78xx_write_reg(struct lan78xx_net *dev, u32 index, u32 data) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL); 4598c2ecf20Sopenharmony_ci int ret; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (!buf) 4628c2ecf20Sopenharmony_ci return -ENOMEM; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci *buf = data; 4658c2ecf20Sopenharmony_ci cpu_to_le32s(buf); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), 4688c2ecf20Sopenharmony_ci USB_VENDOR_REQUEST_WRITE_REGISTER, 4698c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4708c2ecf20Sopenharmony_ci 0, index, buf, 4, USB_CTRL_SET_TIMEOUT); 4718c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 4728c2ecf20Sopenharmony_ci netdev_warn(dev->net, 4738c2ecf20Sopenharmony_ci "Failed to write register index 0x%08x. ret = %d", 4748c2ecf20Sopenharmony_ci index, ret); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci kfree(buf); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int lan78xx_read_stats(struct lan78xx_net *dev, 4838c2ecf20Sopenharmony_ci struct lan78xx_statstage *data) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci int ret = 0; 4868c2ecf20Sopenharmony_ci int i; 4878c2ecf20Sopenharmony_ci struct lan78xx_statstage *stats; 4888c2ecf20Sopenharmony_ci u32 *src; 4898c2ecf20Sopenharmony_ci u32 *dst; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci stats = kmalloc(sizeof(*stats), GFP_KERNEL); 4928c2ecf20Sopenharmony_ci if (!stats) 4938c2ecf20Sopenharmony_ci return -ENOMEM; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = usb_control_msg(dev->udev, 4968c2ecf20Sopenharmony_ci usb_rcvctrlpipe(dev->udev, 0), 4978c2ecf20Sopenharmony_ci USB_VENDOR_REQUEST_GET_STATS, 4988c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4998c2ecf20Sopenharmony_ci 0, 5008c2ecf20Sopenharmony_ci 0, 5018c2ecf20Sopenharmony_ci (void *)stats, 5028c2ecf20Sopenharmony_ci sizeof(*stats), 5038c2ecf20Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 5048c2ecf20Sopenharmony_ci if (likely(ret >= 0)) { 5058c2ecf20Sopenharmony_ci src = (u32 *)stats; 5068c2ecf20Sopenharmony_ci dst = (u32 *)data; 5078c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(*stats)/sizeof(u32); i++) { 5088c2ecf20Sopenharmony_ci le32_to_cpus(&src[i]); 5098c2ecf20Sopenharmony_ci dst[i] = src[i]; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci } else { 5128c2ecf20Sopenharmony_ci netdev_warn(dev->net, 5138c2ecf20Sopenharmony_ci "Failed to read stat ret = %d", ret); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci kfree(stats); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci#define check_counter_rollover(struct1, dev_stats, member) { \ 5228c2ecf20Sopenharmony_ci if (struct1->member < dev_stats.saved.member) \ 5238c2ecf20Sopenharmony_ci dev_stats.rollover_count.member++; \ 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void lan78xx_check_stat_rollover(struct lan78xx_net *dev, 5278c2ecf20Sopenharmony_ci struct lan78xx_statstage *stats) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_fcs_errors); 5308c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_alignment_errors); 5318c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_fragment_errors); 5328c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_jabber_errors); 5338c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_undersize_frame_errors); 5348c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_oversize_frame_errors); 5358c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_dropped_frames); 5368c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_unicast_byte_count); 5378c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_broadcast_byte_count); 5388c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_multicast_byte_count); 5398c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_unicast_frames); 5408c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_broadcast_frames); 5418c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_multicast_frames); 5428c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_pause_frames); 5438c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_64_byte_frames); 5448c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_65_127_byte_frames); 5458c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_128_255_byte_frames); 5468c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_256_511_bytes_frames); 5478c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_512_1023_byte_frames); 5488c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_1024_1518_byte_frames); 5498c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, rx_greater_1518_byte_frames); 5508c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, eee_rx_lpi_transitions); 5518c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, eee_rx_lpi_time); 5528c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_fcs_errors); 5538c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_excess_deferral_errors); 5548c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_carrier_errors); 5558c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_bad_byte_count); 5568c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_single_collisions); 5578c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_multiple_collisions); 5588c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_excessive_collision); 5598c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_late_collisions); 5608c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_unicast_byte_count); 5618c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_broadcast_byte_count); 5628c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_multicast_byte_count); 5638c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_unicast_frames); 5648c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_broadcast_frames); 5658c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_multicast_frames); 5668c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_pause_frames); 5678c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_64_byte_frames); 5688c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_65_127_byte_frames); 5698c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_128_255_byte_frames); 5708c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_256_511_bytes_frames); 5718c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_512_1023_byte_frames); 5728c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_1024_1518_byte_frames); 5738c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, tx_greater_1518_byte_frames); 5748c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, eee_tx_lpi_transitions); 5758c2ecf20Sopenharmony_ci check_counter_rollover(stats, dev->stats, eee_tx_lpi_time); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci memcpy(&dev->stats.saved, stats, sizeof(struct lan78xx_statstage)); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic void lan78xx_update_stats(struct lan78xx_net *dev) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci u32 *p, *count, *max; 5838c2ecf20Sopenharmony_ci u64 *data; 5848c2ecf20Sopenharmony_ci int i; 5858c2ecf20Sopenharmony_ci struct lan78xx_statstage lan78xx_stats; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (usb_autopm_get_interface(dev->intf) < 0) 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci p = (u32 *)&lan78xx_stats; 5918c2ecf20Sopenharmony_ci count = (u32 *)&dev->stats.rollover_count; 5928c2ecf20Sopenharmony_ci max = (u32 *)&dev->stats.rollover_max; 5938c2ecf20Sopenharmony_ci data = (u64 *)&dev->stats.curr_stat; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci mutex_lock(&dev->stats.access_lock); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (lan78xx_read_stats(dev, &lan78xx_stats) > 0) 5988c2ecf20Sopenharmony_ci lan78xx_check_stat_rollover(dev, &lan78xx_stats); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci for (i = 0; i < (sizeof(lan78xx_stats) / (sizeof(u32))); i++) 6018c2ecf20Sopenharmony_ci data[i] = (u64)p[i] + ((u64)count[i] * ((u64)max[i] + 1)); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci mutex_unlock(&dev->stats.access_lock); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/* Loop until the read is completed with timeout called with phy_mutex held */ 6098c2ecf20Sopenharmony_cistatic int lan78xx_phy_wait_not_busy(struct lan78xx_net *dev) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 6128c2ecf20Sopenharmony_ci u32 val; 6138c2ecf20Sopenharmony_ci int ret; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci do { 6168c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MII_ACC, &val); 6178c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 6188c2ecf20Sopenharmony_ci return -EIO; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (!(val & MII_ACC_MII_BUSY_)) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci } while (!time_after(jiffies, start_time + HZ)); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return -EIO; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic inline u32 mii_access(int id, int index, int read) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci u32 ret; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ret = ((u32)id << MII_ACC_PHY_ADDR_SHIFT_) & MII_ACC_PHY_ADDR_MASK_; 6328c2ecf20Sopenharmony_ci ret |= ((u32)index << MII_ACC_MIIRINDA_SHIFT_) & MII_ACC_MIIRINDA_MASK_; 6338c2ecf20Sopenharmony_ci if (read) 6348c2ecf20Sopenharmony_ci ret |= MII_ACC_MII_READ_; 6358c2ecf20Sopenharmony_ci else 6368c2ecf20Sopenharmony_ci ret |= MII_ACC_MII_WRITE_; 6378c2ecf20Sopenharmony_ci ret |= MII_ACC_MII_BUSY_; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic int lan78xx_wait_eeprom(struct lan78xx_net *dev) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 6458c2ecf20Sopenharmony_ci u32 val; 6468c2ecf20Sopenharmony_ci int ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci do { 6498c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, E2P_CMD, &val); 6508c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 6518c2ecf20Sopenharmony_ci return -EIO; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (!(val & E2P_CMD_EPC_BUSY_) || 6548c2ecf20Sopenharmony_ci (val & E2P_CMD_EPC_TIMEOUT_)) 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci usleep_range(40, 100); 6578c2ecf20Sopenharmony_ci } while (!time_after(jiffies, start_time + HZ)); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (val & (E2P_CMD_EPC_TIMEOUT_ | E2P_CMD_EPC_BUSY_)) { 6608c2ecf20Sopenharmony_ci netdev_warn(dev->net, "EEPROM read operation timeout"); 6618c2ecf20Sopenharmony_ci return -EIO; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int lan78xx_eeprom_confirm_not_busy(struct lan78xx_net *dev) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci unsigned long start_time = jiffies; 6708c2ecf20Sopenharmony_ci u32 val; 6718c2ecf20Sopenharmony_ci int ret; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci do { 6748c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, E2P_CMD, &val); 6758c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 6768c2ecf20Sopenharmony_ci return -EIO; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (!(val & E2P_CMD_EPC_BUSY_)) 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci usleep_range(40, 100); 6828c2ecf20Sopenharmony_ci } while (!time_after(jiffies, start_time + HZ)); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci netdev_warn(dev->net, "EEPROM is busy"); 6858c2ecf20Sopenharmony_ci return -EIO; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset, 6898c2ecf20Sopenharmony_ci u32 length, u8 *data) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci u32 val; 6928c2ecf20Sopenharmony_ci u32 saved; 6938c2ecf20Sopenharmony_ci int i, ret; 6948c2ecf20Sopenharmony_ci int retval; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* depends on chip, some EEPROM pins are muxed with LED function. 6978c2ecf20Sopenharmony_ci * disable & restore LED function to access EEPROM. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, HW_CFG, &val); 7008c2ecf20Sopenharmony_ci saved = val; 7018c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7800_) { 7028c2ecf20Sopenharmony_ci val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_); 7038c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, val); 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci retval = lan78xx_eeprom_confirm_not_busy(dev); 7078c2ecf20Sopenharmony_ci if (retval) 7088c2ecf20Sopenharmony_ci return retval; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 7118c2ecf20Sopenharmony_ci val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_; 7128c2ecf20Sopenharmony_ci val |= (offset & E2P_CMD_EPC_ADDR_MASK_); 7138c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, E2P_CMD, val); 7148c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 7158c2ecf20Sopenharmony_ci retval = -EIO; 7168c2ecf20Sopenharmony_ci goto exit; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci retval = lan78xx_wait_eeprom(dev); 7208c2ecf20Sopenharmony_ci if (retval < 0) 7218c2ecf20Sopenharmony_ci goto exit; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, E2P_DATA, &val); 7248c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 7258c2ecf20Sopenharmony_ci retval = -EIO; 7268c2ecf20Sopenharmony_ci goto exit; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci data[i] = val & 0xFF; 7308c2ecf20Sopenharmony_ci offset++; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci retval = 0; 7348c2ecf20Sopenharmony_ciexit: 7358c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7800_) 7368c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, saved); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return retval; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int lan78xx_read_eeprom(struct lan78xx_net *dev, u32 offset, 7428c2ecf20Sopenharmony_ci u32 length, u8 *data) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci u8 sig; 7458c2ecf20Sopenharmony_ci int ret; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig); 7488c2ecf20Sopenharmony_ci if ((ret == 0) && (sig == EEPROM_INDICATOR)) 7498c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_eeprom(dev, offset, length, data); 7508c2ecf20Sopenharmony_ci else 7518c2ecf20Sopenharmony_ci ret = -EINVAL; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset, 7578c2ecf20Sopenharmony_ci u32 length, u8 *data) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci u32 val; 7608c2ecf20Sopenharmony_ci u32 saved; 7618c2ecf20Sopenharmony_ci int i, ret; 7628c2ecf20Sopenharmony_ci int retval; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* depends on chip, some EEPROM pins are muxed with LED function. 7658c2ecf20Sopenharmony_ci * disable & restore LED function to access EEPROM. 7668c2ecf20Sopenharmony_ci */ 7678c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, HW_CFG, &val); 7688c2ecf20Sopenharmony_ci saved = val; 7698c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7800_) { 7708c2ecf20Sopenharmony_ci val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_); 7718c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, val); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci retval = lan78xx_eeprom_confirm_not_busy(dev); 7758c2ecf20Sopenharmony_ci if (retval) 7768c2ecf20Sopenharmony_ci goto exit; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Issue write/erase enable command */ 7798c2ecf20Sopenharmony_ci val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_; 7808c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, E2P_CMD, val); 7818c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 7828c2ecf20Sopenharmony_ci retval = -EIO; 7838c2ecf20Sopenharmony_ci goto exit; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci retval = lan78xx_wait_eeprom(dev); 7878c2ecf20Sopenharmony_ci if (retval < 0) 7888c2ecf20Sopenharmony_ci goto exit; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 7918c2ecf20Sopenharmony_ci /* Fill data register */ 7928c2ecf20Sopenharmony_ci val = data[i]; 7938c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, E2P_DATA, val); 7948c2ecf20Sopenharmony_ci if (ret < 0) { 7958c2ecf20Sopenharmony_ci retval = -EIO; 7968c2ecf20Sopenharmony_ci goto exit; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Send "write" command */ 8008c2ecf20Sopenharmony_ci val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_; 8018c2ecf20Sopenharmony_ci val |= (offset & E2P_CMD_EPC_ADDR_MASK_); 8028c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, E2P_CMD, val); 8038c2ecf20Sopenharmony_ci if (ret < 0) { 8048c2ecf20Sopenharmony_ci retval = -EIO; 8058c2ecf20Sopenharmony_ci goto exit; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci retval = lan78xx_wait_eeprom(dev); 8098c2ecf20Sopenharmony_ci if (retval < 0) 8108c2ecf20Sopenharmony_ci goto exit; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci offset++; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci retval = 0; 8168c2ecf20Sopenharmony_ciexit: 8178c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7800_) 8188c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, saved); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return retval; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset, 8248c2ecf20Sopenharmony_ci u32 length, u8 *data) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci int i; 8278c2ecf20Sopenharmony_ci u32 buf; 8288c2ecf20Sopenharmony_ci unsigned long timeout; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_PWR_DN, &buf); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (buf & OTP_PWR_DN_PWRDN_N_) { 8338c2ecf20Sopenharmony_ci /* clear it and wait to be cleared */ 8348c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_PWR_DN, 0); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 8378c2ecf20Sopenharmony_ci do { 8388c2ecf20Sopenharmony_ci usleep_range(1, 10); 8398c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_PWR_DN, &buf); 8408c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 8418c2ecf20Sopenharmony_ci netdev_warn(dev->net, 8428c2ecf20Sopenharmony_ci "timeout on OTP_PWR_DN"); 8438c2ecf20Sopenharmony_ci return -EIO; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci } while (buf & OTP_PWR_DN_PWRDN_N_); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 8498c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_ADDR1, 8508c2ecf20Sopenharmony_ci ((offset + i) >> 8) & OTP_ADDR1_15_11); 8518c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_ADDR2, 8528c2ecf20Sopenharmony_ci ((offset + i) & OTP_ADDR2_10_3)); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_FUNC_CMD, OTP_FUNC_CMD_READ_); 8558c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 8588c2ecf20Sopenharmony_ci do { 8598c2ecf20Sopenharmony_ci udelay(1); 8608c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_STATUS, &buf); 8618c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 8628c2ecf20Sopenharmony_ci netdev_warn(dev->net, 8638c2ecf20Sopenharmony_ci "timeout on OTP_STATUS"); 8648c2ecf20Sopenharmony_ci return -EIO; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci } while (buf & OTP_STATUS_BUSY_); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_RD_DATA, &buf); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci data[i] = (u8)(buf & 0xFF); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return 0; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic int lan78xx_write_raw_otp(struct lan78xx_net *dev, u32 offset, 8778c2ecf20Sopenharmony_ci u32 length, u8 *data) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci int i; 8808c2ecf20Sopenharmony_ci u32 buf; 8818c2ecf20Sopenharmony_ci unsigned long timeout; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_PWR_DN, &buf); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (buf & OTP_PWR_DN_PWRDN_N_) { 8868c2ecf20Sopenharmony_ci /* clear it and wait to be cleared */ 8878c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_PWR_DN, 0); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 8908c2ecf20Sopenharmony_ci do { 8918c2ecf20Sopenharmony_ci udelay(1); 8928c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_PWR_DN, &buf); 8938c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 8948c2ecf20Sopenharmony_ci netdev_warn(dev->net, 8958c2ecf20Sopenharmony_ci "timeout on OTP_PWR_DN completion"); 8968c2ecf20Sopenharmony_ci return -EIO; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci } while (buf & OTP_PWR_DN_PWRDN_N_); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* set to BYTE program mode */ 9028c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 9058c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_ADDR1, 9068c2ecf20Sopenharmony_ci ((offset + i) >> 8) & OTP_ADDR1_15_11); 9078c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_ADDR2, 9088c2ecf20Sopenharmony_ci ((offset + i) & OTP_ADDR2_10_3)); 9098c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_PRGM_DATA, data[i]); 9108c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_); 9118c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 9148c2ecf20Sopenharmony_ci do { 9158c2ecf20Sopenharmony_ci udelay(1); 9168c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, OTP_STATUS, &buf); 9178c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 9188c2ecf20Sopenharmony_ci netdev_warn(dev->net, 9198c2ecf20Sopenharmony_ci "Timeout on OTP_STATUS completion"); 9208c2ecf20Sopenharmony_ci return -EIO; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci } while (buf & OTP_STATUS_BUSY_); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci return 0; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset, 9298c2ecf20Sopenharmony_ci u32 length, u8 *data) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci u8 sig; 9328c2ecf20Sopenharmony_ci int ret; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_otp(dev, 0, 1, &sig); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (ret == 0) { 9378c2ecf20Sopenharmony_ci if (sig == OTP_INDICATOR_2) 9388c2ecf20Sopenharmony_ci offset += 0x100; 9398c2ecf20Sopenharmony_ci else if (sig != OTP_INDICATOR_1) 9408c2ecf20Sopenharmony_ci ret = -EINVAL; 9418c2ecf20Sopenharmony_ci if (!ret) 9428c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_otp(dev, offset, length, data); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci return ret; 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic int lan78xx_dataport_wait_not_busy(struct lan78xx_net *dev) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci int i, ret; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 9538c2ecf20Sopenharmony_ci u32 dp_sel; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, DP_SEL, &dp_sel); 9568c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 9578c2ecf20Sopenharmony_ci return -EIO; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (dp_sel & DP_SEL_DPRDY_) 9608c2ecf20Sopenharmony_ci return 0; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci usleep_range(40, 100); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci netdev_warn(dev->net, "lan78xx_dataport_wait_not_busy timed out"); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return -EIO; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int lan78xx_dataport_write(struct lan78xx_net *dev, u32 ram_select, 9718c2ecf20Sopenharmony_ci u32 addr, u32 length, u32 *buf) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 9748c2ecf20Sopenharmony_ci u32 dp_sel; 9758c2ecf20Sopenharmony_ci int i, ret; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (usb_autopm_get_interface(dev->intf) < 0) 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci mutex_lock(&pdata->dataport_mutex); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci ret = lan78xx_dataport_wait_not_busy(dev); 9838c2ecf20Sopenharmony_ci if (ret < 0) 9848c2ecf20Sopenharmony_ci goto done; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, DP_SEL, &dp_sel); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci dp_sel &= ~DP_SEL_RSEL_MASK_; 9898c2ecf20Sopenharmony_ci dp_sel |= ram_select; 9908c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, DP_SEL, dp_sel); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 9938c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, DP_ADDR, addr + i); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, DP_DATA, buf[i]); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, DP_CMD, DP_CMD_WRITE_); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci ret = lan78xx_dataport_wait_not_busy(dev); 10008c2ecf20Sopenharmony_ci if (ret < 0) 10018c2ecf20Sopenharmony_ci goto done; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cidone: 10058c2ecf20Sopenharmony_ci mutex_unlock(&pdata->dataport_mutex); 10068c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci return ret; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic void lan78xx_set_addr_filter(struct lan78xx_priv *pdata, 10128c2ecf20Sopenharmony_ci int index, u8 addr[ETH_ALEN]) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci u32 temp; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if ((pdata) && (index > 0) && (index < NUM_OF_MAF)) { 10178c2ecf20Sopenharmony_ci temp = addr[3]; 10188c2ecf20Sopenharmony_ci temp = addr[2] | (temp << 8); 10198c2ecf20Sopenharmony_ci temp = addr[1] | (temp << 8); 10208c2ecf20Sopenharmony_ci temp = addr[0] | (temp << 8); 10218c2ecf20Sopenharmony_ci pdata->pfilter_table[index][1] = temp; 10228c2ecf20Sopenharmony_ci temp = addr[5]; 10238c2ecf20Sopenharmony_ci temp = addr[4] | (temp << 8); 10248c2ecf20Sopenharmony_ci temp |= MAF_HI_VALID_ | MAF_HI_TYPE_DST_; 10258c2ecf20Sopenharmony_ci pdata->pfilter_table[index][0] = temp; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci/* returns hash bit number for given MAC address */ 10308c2ecf20Sopenharmony_cistatic inline u32 lan78xx_hash(char addr[ETH_ALEN]) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic void lan78xx_deferred_multicast_write(struct work_struct *param) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = 10388c2ecf20Sopenharmony_ci container_of(param, struct lan78xx_priv, set_multicast); 10398c2ecf20Sopenharmony_ci struct lan78xx_net *dev = pdata->dev; 10408c2ecf20Sopenharmony_ci int i; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n", 10438c2ecf20Sopenharmony_ci pdata->rfe_ctl); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, DP_SEL_VHF_VLAN_LEN, 10468c2ecf20Sopenharmony_ci DP_SEL_VHF_HASH_LEN, pdata->mchash_table); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci for (i = 1; i < NUM_OF_MAF; i++) { 10498c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_HI(i), 0); 10508c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_LO(i), 10518c2ecf20Sopenharmony_ci pdata->pfilter_table[i][1]); 10528c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_HI(i), 10538c2ecf20Sopenharmony_ci pdata->pfilter_table[i][0]); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic void lan78xx_set_multicast(struct net_device *netdev) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 10628c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 10638c2ecf20Sopenharmony_ci unsigned long flags; 10648c2ecf20Sopenharmony_ci int i; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci pdata->rfe_ctl &= ~(RFE_CTL_UCAST_EN_ | RFE_CTL_MCAST_EN_ | 10698c2ecf20Sopenharmony_ci RFE_CTL_DA_PERFECT_ | RFE_CTL_MCAST_HASH_); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++) 10728c2ecf20Sopenharmony_ci pdata->mchash_table[i] = 0; 10738c2ecf20Sopenharmony_ci /* pfilter_table[0] has own HW address */ 10748c2ecf20Sopenharmony_ci for (i = 1; i < NUM_OF_MAF; i++) { 10758c2ecf20Sopenharmony_ci pdata->pfilter_table[i][0] = 10768c2ecf20Sopenharmony_ci pdata->pfilter_table[i][1] = 0; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_BCAST_EN_; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (dev->net->flags & IFF_PROMISC) { 10828c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "promiscuous mode enabled"); 10838c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_MCAST_EN_ | RFE_CTL_UCAST_EN_; 10848c2ecf20Sopenharmony_ci } else { 10858c2ecf20Sopenharmony_ci if (dev->net->flags & IFF_ALLMULTI) { 10868c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, 10878c2ecf20Sopenharmony_ci "receive all multicast enabled"); 10888c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_MCAST_EN_; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (netdev_mc_count(dev->net)) { 10938c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 10948c2ecf20Sopenharmony_ci int i; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci netif_dbg(dev, drv, dev->net, "receive multicast hash filter"); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_DA_PERFECT_; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci i = 1; 11018c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 11028c2ecf20Sopenharmony_ci /* set first 32 into Perfect Filter */ 11038c2ecf20Sopenharmony_ci if (i < 33) { 11048c2ecf20Sopenharmony_ci lan78xx_set_addr_filter(pdata, i, ha->addr); 11058c2ecf20Sopenharmony_ci } else { 11068c2ecf20Sopenharmony_ci u32 bitnum = lan78xx_hash(ha->addr); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci pdata->mchash_table[bitnum / 32] |= 11098c2ecf20Sopenharmony_ci (1 << (bitnum % 32)); 11108c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_MCAST_HASH_; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci i++; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* defer register writes to a sleepable context */ 11198c2ecf20Sopenharmony_ci schedule_work(&pdata->set_multicast); 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cistatic int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, 11238c2ecf20Sopenharmony_ci u16 lcladv, u16 rmtadv) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci u32 flow = 0, fct_flow = 0; 11268c2ecf20Sopenharmony_ci u8 cap; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (dev->fc_autoneg) 11298c2ecf20Sopenharmony_ci cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); 11308c2ecf20Sopenharmony_ci else 11318c2ecf20Sopenharmony_ci cap = dev->fc_request_control; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (cap & FLOW_CTRL_TX) 11348c2ecf20Sopenharmony_ci flow |= (FLOW_CR_TX_FCEN_ | 0xFFFF); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (cap & FLOW_CTRL_RX) 11378c2ecf20Sopenharmony_ci flow |= FLOW_CR_RX_FCEN_; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (dev->udev->speed == USB_SPEED_SUPER) 11408c2ecf20Sopenharmony_ci fct_flow = 0x817; 11418c2ecf20Sopenharmony_ci else if (dev->udev->speed == USB_SPEED_HIGH) 11428c2ecf20Sopenharmony_ci fct_flow = 0x211; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s", 11458c2ecf20Sopenharmony_ci (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), 11468c2ecf20Sopenharmony_ci (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, FCT_FLOW, fct_flow); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* threshold value should be set before enabling flow */ 11518c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, FLOW, flow); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci return 0; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int lan78xx_link_reset(struct lan78xx_net *dev) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->net->phydev; 11598c2ecf20Sopenharmony_ci struct ethtool_link_ksettings ecmd; 11608c2ecf20Sopenharmony_ci int ladv, radv, ret, link; 11618c2ecf20Sopenharmony_ci u32 buf; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* clear LAN78xx interrupt status */ 11648c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_); 11658c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 11668c2ecf20Sopenharmony_ci return -EIO; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci mutex_lock(&phydev->lock); 11698c2ecf20Sopenharmony_ci phy_read_status(phydev); 11708c2ecf20Sopenharmony_ci link = phydev->link; 11718c2ecf20Sopenharmony_ci mutex_unlock(&phydev->lock); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (!link && dev->link_on) { 11748c2ecf20Sopenharmony_ci dev->link_on = false; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* reset MAC */ 11778c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_CR, &buf); 11788c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 11798c2ecf20Sopenharmony_ci return -EIO; 11808c2ecf20Sopenharmony_ci buf |= MAC_CR_RST_; 11818c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_CR, buf); 11828c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 11838c2ecf20Sopenharmony_ci return -EIO; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci del_timer(&dev->stat_monitor); 11868c2ecf20Sopenharmony_ci } else if (link && !dev->link_on) { 11878c2ecf20Sopenharmony_ci dev->link_on = true; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(phydev, &ecmd); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (dev->udev->speed == USB_SPEED_SUPER) { 11928c2ecf20Sopenharmony_ci if (ecmd.base.speed == 1000) { 11938c2ecf20Sopenharmony_ci /* disable U2 */ 11948c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG1, &buf); 11958c2ecf20Sopenharmony_ci buf &= ~USB_CFG1_DEV_U2_INIT_EN_; 11968c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, USB_CFG1, buf); 11978c2ecf20Sopenharmony_ci /* enable U1 */ 11988c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG1, &buf); 11998c2ecf20Sopenharmony_ci buf |= USB_CFG1_DEV_U1_INIT_EN_; 12008c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, USB_CFG1, buf); 12018c2ecf20Sopenharmony_ci } else { 12028c2ecf20Sopenharmony_ci /* enable U1 & U2 */ 12038c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG1, &buf); 12048c2ecf20Sopenharmony_ci buf |= USB_CFG1_DEV_U2_INIT_EN_; 12058c2ecf20Sopenharmony_ci buf |= USB_CFG1_DEV_U1_INIT_EN_; 12068c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, USB_CFG1, buf); 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci ladv = phy_read(phydev, MII_ADVERTISE); 12118c2ecf20Sopenharmony_ci if (ladv < 0) 12128c2ecf20Sopenharmony_ci return ladv; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci radv = phy_read(phydev, MII_LPA); 12158c2ecf20Sopenharmony_ci if (radv < 0) 12168c2ecf20Sopenharmony_ci return radv; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, 12198c2ecf20Sopenharmony_ci "speed: %u duplex: %d anadv: 0x%04x anlpa: 0x%04x", 12208c2ecf20Sopenharmony_ci ecmd.base.speed, ecmd.base.duplex, ladv, radv); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv, 12238c2ecf20Sopenharmony_ci radv); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (!timer_pending(&dev->stat_monitor)) { 12268c2ecf20Sopenharmony_ci dev->delta = 1; 12278c2ecf20Sopenharmony_ci mod_timer(&dev->stat_monitor, 12288c2ecf20Sopenharmony_ci jiffies + STAT_UPDATE_TIMER); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return ret; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci/* some work can't be done in tasklets, so we use keventd 12388c2ecf20Sopenharmony_ci * 12398c2ecf20Sopenharmony_ci * NOTE: annoying asymmetry: if it's active, schedule_work() fails, 12408c2ecf20Sopenharmony_ci * but tasklet_schedule() doesn't. hope the failure is rare. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_cistatic void lan78xx_defer_kevent(struct lan78xx_net *dev, int work) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci set_bit(work, &dev->flags); 12458c2ecf20Sopenharmony_ci if (!schedule_delayed_work(&dev->wq, 0)) 12468c2ecf20Sopenharmony_ci netdev_err(dev->net, "kevent %d may have been dropped\n", work); 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void lan78xx_status(struct lan78xx_net *dev, struct urb *urb) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci u32 intdata; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (urb->actual_length != 4) { 12548c2ecf20Sopenharmony_ci netdev_warn(dev->net, 12558c2ecf20Sopenharmony_ci "unexpected urb length %d", urb->actual_length); 12568c2ecf20Sopenharmony_ci return; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci intdata = get_unaligned_le32(urb->transfer_buffer); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (intdata & INT_ENP_PHY_INT) { 12628c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata); 12638c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_LINK_RESET); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (dev->domain_data.phyirq > 0) { 12668c2ecf20Sopenharmony_ci local_irq_disable(); 12678c2ecf20Sopenharmony_ci generic_handle_irq(dev->domain_data.phyirq); 12688c2ecf20Sopenharmony_ci local_irq_enable(); 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci } else 12718c2ecf20Sopenharmony_ci netdev_warn(dev->net, 12728c2ecf20Sopenharmony_ci "unexpected interrupt: 0x%08x\n", intdata); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic int lan78xx_ethtool_get_eeprom_len(struct net_device *netdev) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci return MAX_EEPROM_SIZE; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic int lan78xx_ethtool_get_eeprom(struct net_device *netdev, 12818c2ecf20Sopenharmony_ci struct ethtool_eeprom *ee, u8 *data) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 12848c2ecf20Sopenharmony_ci int ret; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 12878c2ecf20Sopenharmony_ci if (ret) 12888c2ecf20Sopenharmony_ci return ret; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci ee->magic = LAN78XX_EEPROM_MAGIC; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_eeprom(dev, ee->offset, ee->len, data); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci return ret; 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic int lan78xx_ethtool_set_eeprom(struct net_device *netdev, 13008c2ecf20Sopenharmony_ci struct ethtool_eeprom *ee, u8 *data) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 13038c2ecf20Sopenharmony_ci int ret; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 13068c2ecf20Sopenharmony_ci if (ret) 13078c2ecf20Sopenharmony_ci return ret; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci /* Invalid EEPROM_INDICATOR at offset zero will result in a failure 13108c2ecf20Sopenharmony_ci * to load data from EEPROM 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_ci if (ee->magic == LAN78XX_EEPROM_MAGIC) 13138c2ecf20Sopenharmony_ci ret = lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data); 13148c2ecf20Sopenharmony_ci else if ((ee->magic == LAN78XX_OTP_MAGIC) && 13158c2ecf20Sopenharmony_ci (ee->offset == 0) && 13168c2ecf20Sopenharmony_ci (ee->len == 512) && 13178c2ecf20Sopenharmony_ci (data[0] == OTP_INDICATOR_1)) 13188c2ecf20Sopenharmony_ci ret = lan78xx_write_raw_otp(dev, ee->offset, ee->len, data); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci return ret; 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_cistatic void lan78xx_get_strings(struct net_device *netdev, u32 stringset, 13268c2ecf20Sopenharmony_ci u8 *data) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci if (stringset == ETH_SS_STATS) 13298c2ecf20Sopenharmony_ci memcpy(data, lan78xx_gstrings, sizeof(lan78xx_gstrings)); 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic int lan78xx_get_sset_count(struct net_device *netdev, int sset) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) 13358c2ecf20Sopenharmony_ci return ARRAY_SIZE(lan78xx_gstrings); 13368c2ecf20Sopenharmony_ci else 13378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic void lan78xx_get_stats(struct net_device *netdev, 13418c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci lan78xx_update_stats(dev); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci mutex_lock(&dev->stats.access_lock); 13488c2ecf20Sopenharmony_ci memcpy(data, &dev->stats.curr_stat, sizeof(dev->stats.curr_stat)); 13498c2ecf20Sopenharmony_ci mutex_unlock(&dev->stats.access_lock); 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic void lan78xx_get_wol(struct net_device *netdev, 13538c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 13568c2ecf20Sopenharmony_ci int ret; 13578c2ecf20Sopenharmony_ci u32 buf; 13588c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if (usb_autopm_get_interface(dev->intf) < 0) 13618c2ecf20Sopenharmony_ci return; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG0, &buf); 13648c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 13658c2ecf20Sopenharmony_ci wol->supported = 0; 13668c2ecf20Sopenharmony_ci wol->wolopts = 0; 13678c2ecf20Sopenharmony_ci } else { 13688c2ecf20Sopenharmony_ci if (buf & USB_CFG_RMT_WKP_) { 13698c2ecf20Sopenharmony_ci wol->supported = WAKE_ALL; 13708c2ecf20Sopenharmony_ci wol->wolopts = pdata->wol; 13718c2ecf20Sopenharmony_ci } else { 13728c2ecf20Sopenharmony_ci wol->supported = 0; 13738c2ecf20Sopenharmony_ci wol->wolopts = 0; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic int lan78xx_set_wol(struct net_device *netdev, 13818c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 13848c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 13858c2ecf20Sopenharmony_ci int ret; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 13888c2ecf20Sopenharmony_ci if (ret < 0) 13898c2ecf20Sopenharmony_ci return ret; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci if (wol->wolopts & ~WAKE_ALL) 13928c2ecf20Sopenharmony_ci return -EINVAL; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci pdata->wol = wol->wolopts; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci phy_ethtool_set_wol(netdev->phydev, wol); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci return ret; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic int lan78xx_get_eee(struct net_device *net, struct ethtool_eee *edata) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 14088c2ecf20Sopenharmony_ci struct phy_device *phydev = net->phydev; 14098c2ecf20Sopenharmony_ci int ret; 14108c2ecf20Sopenharmony_ci u32 buf; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 14138c2ecf20Sopenharmony_ci if (ret < 0) 14148c2ecf20Sopenharmony_ci return ret; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci ret = phy_ethtool_get_eee(phydev, edata); 14178c2ecf20Sopenharmony_ci if (ret < 0) 14188c2ecf20Sopenharmony_ci goto exit; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_CR, &buf); 14218c2ecf20Sopenharmony_ci if (buf & MAC_CR_EEE_EN_) { 14228c2ecf20Sopenharmony_ci edata->eee_enabled = true; 14238c2ecf20Sopenharmony_ci edata->eee_active = !!(edata->advertised & 14248c2ecf20Sopenharmony_ci edata->lp_advertised); 14258c2ecf20Sopenharmony_ci edata->tx_lpi_enabled = true; 14268c2ecf20Sopenharmony_ci /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */ 14278c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf); 14288c2ecf20Sopenharmony_ci edata->tx_lpi_timer = buf; 14298c2ecf20Sopenharmony_ci } else { 14308c2ecf20Sopenharmony_ci edata->eee_enabled = false; 14318c2ecf20Sopenharmony_ci edata->eee_active = false; 14328c2ecf20Sopenharmony_ci edata->tx_lpi_enabled = false; 14338c2ecf20Sopenharmony_ci edata->tx_lpi_timer = 0; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ret = 0; 14378c2ecf20Sopenharmony_ciexit: 14388c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci return ret; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata) 14448c2ecf20Sopenharmony_ci{ 14458c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 14468c2ecf20Sopenharmony_ci int ret; 14478c2ecf20Sopenharmony_ci u32 buf; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 14508c2ecf20Sopenharmony_ci if (ret < 0) 14518c2ecf20Sopenharmony_ci return ret; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (edata->eee_enabled) { 14548c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_CR, &buf); 14558c2ecf20Sopenharmony_ci buf |= MAC_CR_EEE_EN_; 14568c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_CR, buf); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci phy_ethtool_set_eee(net->phydev, edata); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci buf = (u32)edata->tx_lpi_timer; 14618c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf); 14628c2ecf20Sopenharmony_ci } else { 14638c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_CR, &buf); 14648c2ecf20Sopenharmony_ci buf &= ~MAC_CR_EEE_EN_; 14658c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_CR, buf); 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci return 0; 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic u32 lan78xx_get_link(struct net_device *net) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci u32 link; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci mutex_lock(&net->phydev->lock); 14788c2ecf20Sopenharmony_ci phy_read_status(net->phydev); 14798c2ecf20Sopenharmony_ci link = net->phydev->link; 14808c2ecf20Sopenharmony_ci mutex_unlock(&net->phydev->lock); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci return link; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic void lan78xx_get_drvinfo(struct net_device *net, 14868c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 14878c2ecf20Sopenharmony_ci{ 14888c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); 14918c2ecf20Sopenharmony_ci usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic u32 lan78xx_get_msglevel(struct net_device *net) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci return dev->msg_enable; 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_cistatic void lan78xx_set_msglevel(struct net_device *net, u32 level) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci dev->msg_enable = level; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic int lan78xx_get_link_ksettings(struct net_device *net, 15098c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 15108c2ecf20Sopenharmony_ci{ 15118c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 15128c2ecf20Sopenharmony_ci struct phy_device *phydev = net->phydev; 15138c2ecf20Sopenharmony_ci int ret; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 15168c2ecf20Sopenharmony_ci if (ret < 0) 15178c2ecf20Sopenharmony_ci return ret; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(phydev, cmd); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci return ret; 15248c2ecf20Sopenharmony_ci} 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_cistatic int lan78xx_set_link_ksettings(struct net_device *net, 15278c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 15308c2ecf20Sopenharmony_ci struct phy_device *phydev = net->phydev; 15318c2ecf20Sopenharmony_ci int ret = 0; 15328c2ecf20Sopenharmony_ci int temp; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 15358c2ecf20Sopenharmony_ci if (ret < 0) 15368c2ecf20Sopenharmony_ci return ret; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* change speed & duplex */ 15398c2ecf20Sopenharmony_ci ret = phy_ethtool_ksettings_set(phydev, cmd); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (!cmd->base.autoneg) { 15428c2ecf20Sopenharmony_ci /* force link down */ 15438c2ecf20Sopenharmony_ci temp = phy_read(phydev, MII_BMCR); 15448c2ecf20Sopenharmony_ci phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK); 15458c2ecf20Sopenharmony_ci mdelay(1); 15468c2ecf20Sopenharmony_ci phy_write(phydev, MII_BMCR, temp); 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci return ret; 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_cistatic void lan78xx_get_pause(struct net_device *net, 15558c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 15588c2ecf20Sopenharmony_ci struct phy_device *phydev = net->phydev; 15598c2ecf20Sopenharmony_ci struct ethtool_link_ksettings ecmd; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(phydev, &ecmd); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci pause->autoneg = dev->fc_autoneg; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if (dev->fc_request_control & FLOW_CTRL_TX) 15668c2ecf20Sopenharmony_ci pause->tx_pause = 1; 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci if (dev->fc_request_control & FLOW_CTRL_RX) 15698c2ecf20Sopenharmony_ci pause->rx_pause = 1; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistatic int lan78xx_set_pause(struct net_device *net, 15738c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 15768c2ecf20Sopenharmony_ci struct phy_device *phydev = net->phydev; 15778c2ecf20Sopenharmony_ci struct ethtool_link_ksettings ecmd; 15788c2ecf20Sopenharmony_ci int ret; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(phydev, &ecmd); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (pause->autoneg && !ecmd.base.autoneg) { 15838c2ecf20Sopenharmony_ci ret = -EINVAL; 15848c2ecf20Sopenharmony_ci goto exit; 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci dev->fc_request_control = 0; 15888c2ecf20Sopenharmony_ci if (pause->rx_pause) 15898c2ecf20Sopenharmony_ci dev->fc_request_control |= FLOW_CTRL_RX; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (pause->tx_pause) 15928c2ecf20Sopenharmony_ci dev->fc_request_control |= FLOW_CTRL_TX; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (ecmd.base.autoneg) { 15958c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, }; 15968c2ecf20Sopenharmony_ci u32 mii_adv; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, 15998c2ecf20Sopenharmony_ci ecmd.link_modes.advertising); 16008c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 16018c2ecf20Sopenharmony_ci ecmd.link_modes.advertising); 16028c2ecf20Sopenharmony_ci mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); 16038c2ecf20Sopenharmony_ci mii_adv_to_linkmode_adv_t(fc, mii_adv); 16048c2ecf20Sopenharmony_ci linkmode_or(ecmd.link_modes.advertising, fc, 16058c2ecf20Sopenharmony_ci ecmd.link_modes.advertising); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci phy_ethtool_ksettings_set(phydev, &ecmd); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci dev->fc_autoneg = pause->autoneg; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci ret = 0; 16138c2ecf20Sopenharmony_ciexit: 16148c2ecf20Sopenharmony_ci return ret; 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cistatic int lan78xx_get_regs_len(struct net_device *netdev) 16188c2ecf20Sopenharmony_ci{ 16198c2ecf20Sopenharmony_ci if (!netdev->phydev) 16208c2ecf20Sopenharmony_ci return (sizeof(lan78xx_regs)); 16218c2ecf20Sopenharmony_ci else 16228c2ecf20Sopenharmony_ci return (sizeof(lan78xx_regs) + PHY_REG_SIZE); 16238c2ecf20Sopenharmony_ci} 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_cistatic void 16268c2ecf20Sopenharmony_cilan78xx_get_regs(struct net_device *netdev, struct ethtool_regs *regs, 16278c2ecf20Sopenharmony_ci void *buf) 16288c2ecf20Sopenharmony_ci{ 16298c2ecf20Sopenharmony_ci u32 *data = buf; 16308c2ecf20Sopenharmony_ci int i, j; 16318c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci /* Read Device/MAC registers */ 16348c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lan78xx_regs); i++) 16358c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, lan78xx_regs[i], &data[i]); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (!netdev->phydev) 16388c2ecf20Sopenharmony_ci return; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* Read PHY registers */ 16418c2ecf20Sopenharmony_ci for (j = 0; j < 32; i++, j++) 16428c2ecf20Sopenharmony_ci data[i] = phy_read(netdev->phydev, j); 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic const struct ethtool_ops lan78xx_ethtool_ops = { 16468c2ecf20Sopenharmony_ci .get_link = lan78xx_get_link, 16478c2ecf20Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 16488c2ecf20Sopenharmony_ci .get_drvinfo = lan78xx_get_drvinfo, 16498c2ecf20Sopenharmony_ci .get_msglevel = lan78xx_get_msglevel, 16508c2ecf20Sopenharmony_ci .set_msglevel = lan78xx_set_msglevel, 16518c2ecf20Sopenharmony_ci .get_eeprom_len = lan78xx_ethtool_get_eeprom_len, 16528c2ecf20Sopenharmony_ci .get_eeprom = lan78xx_ethtool_get_eeprom, 16538c2ecf20Sopenharmony_ci .set_eeprom = lan78xx_ethtool_set_eeprom, 16548c2ecf20Sopenharmony_ci .get_ethtool_stats = lan78xx_get_stats, 16558c2ecf20Sopenharmony_ci .get_sset_count = lan78xx_get_sset_count, 16568c2ecf20Sopenharmony_ci .get_strings = lan78xx_get_strings, 16578c2ecf20Sopenharmony_ci .get_wol = lan78xx_get_wol, 16588c2ecf20Sopenharmony_ci .set_wol = lan78xx_set_wol, 16598c2ecf20Sopenharmony_ci .get_eee = lan78xx_get_eee, 16608c2ecf20Sopenharmony_ci .set_eee = lan78xx_set_eee, 16618c2ecf20Sopenharmony_ci .get_pauseparam = lan78xx_get_pause, 16628c2ecf20Sopenharmony_ci .set_pauseparam = lan78xx_set_pause, 16638c2ecf20Sopenharmony_ci .get_link_ksettings = lan78xx_get_link_ksettings, 16648c2ecf20Sopenharmony_ci .set_link_ksettings = lan78xx_set_link_ksettings, 16658c2ecf20Sopenharmony_ci .get_regs_len = lan78xx_get_regs_len, 16668c2ecf20Sopenharmony_ci .get_regs = lan78xx_get_regs, 16678c2ecf20Sopenharmony_ci}; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic void lan78xx_init_mac_address(struct lan78xx_net *dev) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci u32 addr_lo, addr_hi; 16728c2ecf20Sopenharmony_ci u8 addr[6]; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, RX_ADDRL, &addr_lo); 16758c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, RX_ADDRH, &addr_hi); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci addr[0] = addr_lo & 0xFF; 16788c2ecf20Sopenharmony_ci addr[1] = (addr_lo >> 8) & 0xFF; 16798c2ecf20Sopenharmony_ci addr[2] = (addr_lo >> 16) & 0xFF; 16808c2ecf20Sopenharmony_ci addr[3] = (addr_lo >> 24) & 0xFF; 16818c2ecf20Sopenharmony_ci addr[4] = addr_hi & 0xFF; 16828c2ecf20Sopenharmony_ci addr[5] = (addr_hi >> 8) & 0xFF; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr)) { 16858c2ecf20Sopenharmony_ci if (!eth_platform_get_mac_address(&dev->udev->dev, addr)) { 16868c2ecf20Sopenharmony_ci /* valid address present in Device Tree */ 16878c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, 16888c2ecf20Sopenharmony_ci "MAC address read from Device Tree"); 16898c2ecf20Sopenharmony_ci } else if (((lan78xx_read_eeprom(dev, EEPROM_MAC_OFFSET, 16908c2ecf20Sopenharmony_ci ETH_ALEN, addr) == 0) || 16918c2ecf20Sopenharmony_ci (lan78xx_read_otp(dev, EEPROM_MAC_OFFSET, 16928c2ecf20Sopenharmony_ci ETH_ALEN, addr) == 0)) && 16938c2ecf20Sopenharmony_ci is_valid_ether_addr(addr)) { 16948c2ecf20Sopenharmony_ci /* eeprom values are valid so use them */ 16958c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, 16968c2ecf20Sopenharmony_ci "MAC address read from EEPROM"); 16978c2ecf20Sopenharmony_ci } else { 16988c2ecf20Sopenharmony_ci /* generate random MAC */ 16998c2ecf20Sopenharmony_ci eth_random_addr(addr); 17008c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, 17018c2ecf20Sopenharmony_ci "MAC address set to random addr"); 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci addr_lo = addr[0] | (addr[1] << 8) | 17058c2ecf20Sopenharmony_ci (addr[2] << 16) | (addr[3] << 24); 17068c2ecf20Sopenharmony_ci addr_hi = addr[4] | (addr[5] << 8); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RX_ADDRL, addr_lo); 17098c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RX_ADDRH, addr_hi); 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_LO(0), addr_lo); 17138c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci ether_addr_copy(dev->net->dev_addr, addr); 17168c2ecf20Sopenharmony_ci} 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci/* MDIO read and write wrappers for phylib */ 17198c2ecf20Sopenharmony_cistatic int lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci struct lan78xx_net *dev = bus->priv; 17228c2ecf20Sopenharmony_ci u32 val, addr; 17238c2ecf20Sopenharmony_ci int ret; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 17268c2ecf20Sopenharmony_ci if (ret < 0) 17278c2ecf20Sopenharmony_ci return ret; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci /* confirm MII not busy */ 17328c2ecf20Sopenharmony_ci ret = lan78xx_phy_wait_not_busy(dev); 17338c2ecf20Sopenharmony_ci if (ret < 0) 17348c2ecf20Sopenharmony_ci goto done; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci /* set the address, index & direction (read from PHY) */ 17378c2ecf20Sopenharmony_ci addr = mii_access(phy_id, idx, MII_READ); 17388c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MII_ACC, addr); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci ret = lan78xx_phy_wait_not_busy(dev); 17418c2ecf20Sopenharmony_ci if (ret < 0) 17428c2ecf20Sopenharmony_ci goto done; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MII_DATA, &val); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci ret = (int)(val & 0xFFFF); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cidone: 17498c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 17508c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci return ret; 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_cistatic int lan78xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx, 17568c2ecf20Sopenharmony_ci u16 regval) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci struct lan78xx_net *dev = bus->priv; 17598c2ecf20Sopenharmony_ci u32 val, addr; 17608c2ecf20Sopenharmony_ci int ret; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 17638c2ecf20Sopenharmony_ci if (ret < 0) 17648c2ecf20Sopenharmony_ci return ret; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci mutex_lock(&dev->phy_mutex); 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci /* confirm MII not busy */ 17698c2ecf20Sopenharmony_ci ret = lan78xx_phy_wait_not_busy(dev); 17708c2ecf20Sopenharmony_ci if (ret < 0) 17718c2ecf20Sopenharmony_ci goto done; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci val = (u32)regval; 17748c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MII_DATA, val); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci /* set the address, index & direction (write to PHY) */ 17778c2ecf20Sopenharmony_ci addr = mii_access(phy_id, idx, MII_WRITE); 17788c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MII_ACC, addr); 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci ret = lan78xx_phy_wait_not_busy(dev); 17818c2ecf20Sopenharmony_ci if (ret < 0) 17828c2ecf20Sopenharmony_ci goto done; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_cidone: 17858c2ecf20Sopenharmony_ci mutex_unlock(&dev->phy_mutex); 17868c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 17878c2ecf20Sopenharmony_ci return 0; 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_cistatic int lan78xx_mdio_init(struct lan78xx_net *dev) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci struct device_node *node; 17938c2ecf20Sopenharmony_ci int ret; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci dev->mdiobus = mdiobus_alloc(); 17968c2ecf20Sopenharmony_ci if (!dev->mdiobus) { 17978c2ecf20Sopenharmony_ci netdev_err(dev->net, "can't allocate MDIO bus\n"); 17988c2ecf20Sopenharmony_ci return -ENOMEM; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci dev->mdiobus->priv = (void *)dev; 18028c2ecf20Sopenharmony_ci dev->mdiobus->read = lan78xx_mdiobus_read; 18038c2ecf20Sopenharmony_ci dev->mdiobus->write = lan78xx_mdiobus_write; 18048c2ecf20Sopenharmony_ci dev->mdiobus->name = "lan78xx-mdiobus"; 18058c2ecf20Sopenharmony_ci dev->mdiobus->parent = &dev->udev->dev; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", 18088c2ecf20Sopenharmony_ci dev->udev->bus->busnum, dev->udev->devnum); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci switch (dev->chipid) { 18118c2ecf20Sopenharmony_ci case ID_REV_CHIP_ID_7800_: 18128c2ecf20Sopenharmony_ci case ID_REV_CHIP_ID_7850_: 18138c2ecf20Sopenharmony_ci /* set to internal PHY id */ 18148c2ecf20Sopenharmony_ci dev->mdiobus->phy_mask = ~(1 << 1); 18158c2ecf20Sopenharmony_ci break; 18168c2ecf20Sopenharmony_ci case ID_REV_CHIP_ID_7801_: 18178c2ecf20Sopenharmony_ci /* scan thru PHYAD[2..0] */ 18188c2ecf20Sopenharmony_ci dev->mdiobus->phy_mask = ~(0xFF); 18198c2ecf20Sopenharmony_ci break; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci node = of_get_child_by_name(dev->udev->dev.of_node, "mdio"); 18238c2ecf20Sopenharmony_ci ret = of_mdiobus_register(dev->mdiobus, node); 18248c2ecf20Sopenharmony_ci of_node_put(node); 18258c2ecf20Sopenharmony_ci if (ret) { 18268c2ecf20Sopenharmony_ci netdev_err(dev->net, "can't register MDIO bus\n"); 18278c2ecf20Sopenharmony_ci goto exit1; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id); 18318c2ecf20Sopenharmony_ci return 0; 18328c2ecf20Sopenharmony_ciexit1: 18338c2ecf20Sopenharmony_ci mdiobus_free(dev->mdiobus); 18348c2ecf20Sopenharmony_ci return ret; 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_cistatic void lan78xx_remove_mdio(struct lan78xx_net *dev) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci mdiobus_unregister(dev->mdiobus); 18408c2ecf20Sopenharmony_ci mdiobus_free(dev->mdiobus); 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_cistatic void lan78xx_link_status_change(struct net_device *net) 18448c2ecf20Sopenharmony_ci{ 18458c2ecf20Sopenharmony_ci struct phy_device *phydev = net->phydev; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci phy_print_status(phydev); 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cistatic int irq_map(struct irq_domain *d, unsigned int irq, 18518c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct irq_domain_data *data = d->host_data; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci irq_set_chip_data(irq, data); 18568c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, data->irqchip, data->irq_handler); 18578c2ecf20Sopenharmony_ci irq_set_noprobe(irq); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci return 0; 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic void irq_unmap(struct irq_domain *d, unsigned int irq) 18638c2ecf20Sopenharmony_ci{ 18648c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 18658c2ecf20Sopenharmony_ci irq_set_chip_data(irq, NULL); 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic const struct irq_domain_ops chip_domain_ops = { 18698c2ecf20Sopenharmony_ci .map = irq_map, 18708c2ecf20Sopenharmony_ci .unmap = irq_unmap, 18718c2ecf20Sopenharmony_ci}; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_cistatic void lan78xx_irq_mask(struct irq_data *irqd) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci data->irqenable &= ~BIT(irqd_to_hwirq(irqd)); 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic void lan78xx_irq_unmask(struct irq_data *irqd) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci data->irqenable |= BIT(irqd_to_hwirq(irqd)); 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic void lan78xx_irq_bus_lock(struct irq_data *irqd) 18888c2ecf20Sopenharmony_ci{ 18898c2ecf20Sopenharmony_ci struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci mutex_lock(&data->irq_lock); 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cistatic void lan78xx_irq_bus_sync_unlock(struct irq_data *irqd) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd); 18978c2ecf20Sopenharmony_ci struct lan78xx_net *dev = 18988c2ecf20Sopenharmony_ci container_of(data, struct lan78xx_net, domain_data); 18998c2ecf20Sopenharmony_ci u32 buf; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci /* call register access here because irq_bus_lock & irq_bus_sync_unlock 19028c2ecf20Sopenharmony_ci * are only two callbacks executed in non-atomic contex. 19038c2ecf20Sopenharmony_ci */ 19048c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, INT_EP_CTL, &buf); 19058c2ecf20Sopenharmony_ci if (buf != data->irqenable) 19068c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, INT_EP_CTL, data->irqenable); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci mutex_unlock(&data->irq_lock); 19098c2ecf20Sopenharmony_ci} 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cistatic struct irq_chip lan78xx_irqchip = { 19128c2ecf20Sopenharmony_ci .name = "lan78xx-irqs", 19138c2ecf20Sopenharmony_ci .irq_mask = lan78xx_irq_mask, 19148c2ecf20Sopenharmony_ci .irq_unmask = lan78xx_irq_unmask, 19158c2ecf20Sopenharmony_ci .irq_bus_lock = lan78xx_irq_bus_lock, 19168c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = lan78xx_irq_bus_sync_unlock, 19178c2ecf20Sopenharmony_ci}; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_cistatic int lan78xx_setup_irq_domain(struct lan78xx_net *dev) 19208c2ecf20Sopenharmony_ci{ 19218c2ecf20Sopenharmony_ci struct device_node *of_node; 19228c2ecf20Sopenharmony_ci struct irq_domain *irqdomain; 19238c2ecf20Sopenharmony_ci unsigned int irqmap = 0; 19248c2ecf20Sopenharmony_ci u32 buf; 19258c2ecf20Sopenharmony_ci int ret = 0; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci of_node = dev->udev->dev.parent->of_node; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci mutex_init(&dev->domain_data.irq_lock); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, INT_EP_CTL, &buf); 19328c2ecf20Sopenharmony_ci dev->domain_data.irqenable = buf; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci dev->domain_data.irqchip = &lan78xx_irqchip; 19358c2ecf20Sopenharmony_ci dev->domain_data.irq_handler = handle_simple_irq; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci irqdomain = irq_domain_add_simple(of_node, MAX_INT_EP, 0, 19388c2ecf20Sopenharmony_ci &chip_domain_ops, &dev->domain_data); 19398c2ecf20Sopenharmony_ci if (irqdomain) { 19408c2ecf20Sopenharmony_ci /* create mapping for PHY interrupt */ 19418c2ecf20Sopenharmony_ci irqmap = irq_create_mapping(irqdomain, INT_EP_PHY); 19428c2ecf20Sopenharmony_ci if (!irqmap) { 19438c2ecf20Sopenharmony_ci irq_domain_remove(irqdomain); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci irqdomain = NULL; 19468c2ecf20Sopenharmony_ci ret = -EINVAL; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci } else { 19498c2ecf20Sopenharmony_ci ret = -EINVAL; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci dev->domain_data.irqdomain = irqdomain; 19538c2ecf20Sopenharmony_ci dev->domain_data.phyirq = irqmap; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci return ret; 19568c2ecf20Sopenharmony_ci} 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_cistatic void lan78xx_remove_irq_domain(struct lan78xx_net *dev) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci if (dev->domain_data.phyirq > 0) { 19618c2ecf20Sopenharmony_ci irq_dispose_mapping(dev->domain_data.phyirq); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (dev->domain_data.irqdomain) 19648c2ecf20Sopenharmony_ci irq_domain_remove(dev->domain_data.irqdomain); 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci dev->domain_data.phyirq = 0; 19678c2ecf20Sopenharmony_ci dev->domain_data.irqdomain = NULL; 19688c2ecf20Sopenharmony_ci} 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_cistatic int lan8835_fixup(struct phy_device *phydev) 19718c2ecf20Sopenharmony_ci{ 19728c2ecf20Sopenharmony_ci int buf; 19738c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(phydev->attached_dev); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* LED2/PME_N/IRQ_N/RGMII_ID pin to IRQ_N mode */ 19768c2ecf20Sopenharmony_ci buf = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x8010); 19778c2ecf20Sopenharmony_ci buf &= ~0x1800; 19788c2ecf20Sopenharmony_ci buf |= 0x0800; 19798c2ecf20Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8010, buf); 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci /* RGMII MAC TXC Delay Enable */ 19828c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_RGMII_ID, 19838c2ecf20Sopenharmony_ci MAC_RGMII_ID_TXC_DELAY_EN_); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* RGMII TX DLL Tune Adjust */ 19868c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci dev->interface = PHY_INTERFACE_MODE_RGMII_TXID; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci return 1; 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_cistatic int ksz9031rnx_fixup(struct phy_device *phydev) 19948c2ecf20Sopenharmony_ci{ 19958c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(phydev->attached_dev); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* Micrel9301RNX PHY configuration */ 19988c2ecf20Sopenharmony_ci /* RGMII Control Signal Pad Skew */ 19998c2ecf20Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_WIS, 4, 0x0077); 20008c2ecf20Sopenharmony_ci /* RGMII RX Data Pad Skew */ 20018c2ecf20Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_WIS, 5, 0x7777); 20028c2ecf20Sopenharmony_ci /* RGMII RX Clock Pad Skew */ 20038c2ecf20Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_WIS, 8, 0x1FF); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci dev->interface = PHY_INTERFACE_MODE_RGMII_RXID; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci return 1; 20088c2ecf20Sopenharmony_ci} 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_cistatic struct phy_device *lan7801_phy_init(struct lan78xx_net *dev) 20118c2ecf20Sopenharmony_ci{ 20128c2ecf20Sopenharmony_ci u32 buf; 20138c2ecf20Sopenharmony_ci int ret; 20148c2ecf20Sopenharmony_ci struct fixed_phy_status fphy_status = { 20158c2ecf20Sopenharmony_ci .link = 1, 20168c2ecf20Sopenharmony_ci .speed = SPEED_1000, 20178c2ecf20Sopenharmony_ci .duplex = DUPLEX_FULL, 20188c2ecf20Sopenharmony_ci }; 20198c2ecf20Sopenharmony_ci struct phy_device *phydev; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci phydev = phy_find_first(dev->mdiobus); 20228c2ecf20Sopenharmony_ci if (!phydev) { 20238c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "PHY Not Found!! Registering Fixed PHY\n"); 20248c2ecf20Sopenharmony_ci phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL); 20258c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) { 20268c2ecf20Sopenharmony_ci netdev_err(dev->net, "No PHY/fixed_PHY found\n"); 20278c2ecf20Sopenharmony_ci return NULL; 20288c2ecf20Sopenharmony_ci } 20298c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Registered FIXED PHY\n"); 20308c2ecf20Sopenharmony_ci dev->interface = PHY_INTERFACE_MODE_RGMII; 20318c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_RGMII_ID, 20328c2ecf20Sopenharmony_ci MAC_RGMII_ID_TXC_DELAY_EN_); 20338c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00); 20348c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, HW_CFG, &buf); 20358c2ecf20Sopenharmony_ci buf |= HW_CFG_CLK125_EN_; 20368c2ecf20Sopenharmony_ci buf |= HW_CFG_REFCLK25_EN_; 20378c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, buf); 20388c2ecf20Sopenharmony_ci } else { 20398c2ecf20Sopenharmony_ci if (!phydev->drv) { 20408c2ecf20Sopenharmony_ci netdev_err(dev->net, "no PHY driver found\n"); 20418c2ecf20Sopenharmony_ci return NULL; 20428c2ecf20Sopenharmony_ci } 20438c2ecf20Sopenharmony_ci dev->interface = PHY_INTERFACE_MODE_RGMII; 20448c2ecf20Sopenharmony_ci /* external PHY fixup for KSZ9031RNX */ 20458c2ecf20Sopenharmony_ci ret = phy_register_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0, 20468c2ecf20Sopenharmony_ci ksz9031rnx_fixup); 20478c2ecf20Sopenharmony_ci if (ret < 0) { 20488c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to register fixup for PHY_KSZ9031RNX\n"); 20498c2ecf20Sopenharmony_ci return NULL; 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci /* external PHY fixup for LAN8835 */ 20528c2ecf20Sopenharmony_ci ret = phy_register_fixup_for_uid(PHY_LAN8835, 0xfffffff0, 20538c2ecf20Sopenharmony_ci lan8835_fixup); 20548c2ecf20Sopenharmony_ci if (ret < 0) { 20558c2ecf20Sopenharmony_ci netdev_err(dev->net, "Failed to register fixup for PHY_LAN8835\n"); 20568c2ecf20Sopenharmony_ci return NULL; 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci /* add more external PHY fixup here if needed */ 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci phydev->is_internal = false; 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci return phydev; 20638c2ecf20Sopenharmony_ci} 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_cistatic int lan78xx_phy_init(struct lan78xx_net *dev) 20668c2ecf20Sopenharmony_ci{ 20678c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, }; 20688c2ecf20Sopenharmony_ci int ret; 20698c2ecf20Sopenharmony_ci u32 mii_adv; 20708c2ecf20Sopenharmony_ci struct phy_device *phydev; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci switch (dev->chipid) { 20738c2ecf20Sopenharmony_ci case ID_REV_CHIP_ID_7801_: 20748c2ecf20Sopenharmony_ci phydev = lan7801_phy_init(dev); 20758c2ecf20Sopenharmony_ci if (!phydev) { 20768c2ecf20Sopenharmony_ci netdev_err(dev->net, "lan7801: PHY Init Failed"); 20778c2ecf20Sopenharmony_ci return -EIO; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci break; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci case ID_REV_CHIP_ID_7800_: 20828c2ecf20Sopenharmony_ci case ID_REV_CHIP_ID_7850_: 20838c2ecf20Sopenharmony_ci phydev = phy_find_first(dev->mdiobus); 20848c2ecf20Sopenharmony_ci if (!phydev) { 20858c2ecf20Sopenharmony_ci netdev_err(dev->net, "no PHY found\n"); 20868c2ecf20Sopenharmony_ci return -EIO; 20878c2ecf20Sopenharmony_ci } 20888c2ecf20Sopenharmony_ci phydev->is_internal = true; 20898c2ecf20Sopenharmony_ci dev->interface = PHY_INTERFACE_MODE_GMII; 20908c2ecf20Sopenharmony_ci break; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci default: 20938c2ecf20Sopenharmony_ci netdev_err(dev->net, "Unknown CHIP ID found\n"); 20948c2ecf20Sopenharmony_ci return -EIO; 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci /* if phyirq is not set, use polling mode in phylib */ 20988c2ecf20Sopenharmony_ci if (dev->domain_data.phyirq > 0) 20998c2ecf20Sopenharmony_ci phydev->irq = dev->domain_data.phyirq; 21008c2ecf20Sopenharmony_ci else 21018c2ecf20Sopenharmony_ci phydev->irq = PHY_POLL; 21028c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci /* set to AUTOMDIX */ 21058c2ecf20Sopenharmony_ci phydev->mdix = ETH_TP_MDI_AUTO; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci ret = phy_connect_direct(dev->net, phydev, 21088c2ecf20Sopenharmony_ci lan78xx_link_status_change, 21098c2ecf20Sopenharmony_ci dev->interface); 21108c2ecf20Sopenharmony_ci if (ret) { 21118c2ecf20Sopenharmony_ci netdev_err(dev->net, "can't attach PHY to %s\n", 21128c2ecf20Sopenharmony_ci dev->mdiobus->id); 21138c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7801_) { 21148c2ecf20Sopenharmony_ci if (phy_is_pseudo_fixed_link(phydev)) { 21158c2ecf20Sopenharmony_ci fixed_phy_unregister(phydev); 21168c2ecf20Sopenharmony_ci } else { 21178c2ecf20Sopenharmony_ci phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 21188c2ecf20Sopenharmony_ci 0xfffffff0); 21198c2ecf20Sopenharmony_ci phy_unregister_fixup_for_uid(PHY_LAN8835, 21208c2ecf20Sopenharmony_ci 0xfffffff0); 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci return -EIO; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci /* MAC doesn't support 1000T Half */ 21278c2ecf20Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci /* support both flow controls */ 21308c2ecf20Sopenharmony_ci dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX); 21318c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, 21328c2ecf20Sopenharmony_ci phydev->advertising); 21338c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 21348c2ecf20Sopenharmony_ci phydev->advertising); 21358c2ecf20Sopenharmony_ci mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); 21368c2ecf20Sopenharmony_ci mii_adv_to_linkmode_adv_t(fc, mii_adv); 21378c2ecf20Sopenharmony_ci linkmode_or(phydev->advertising, fc, phydev->advertising); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci if (phydev->mdio.dev.of_node) { 21408c2ecf20Sopenharmony_ci u32 reg; 21418c2ecf20Sopenharmony_ci int len; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci len = of_property_count_elems_of_size(phydev->mdio.dev.of_node, 21448c2ecf20Sopenharmony_ci "microchip,led-modes", 21458c2ecf20Sopenharmony_ci sizeof(u32)); 21468c2ecf20Sopenharmony_ci if (len >= 0) { 21478c2ecf20Sopenharmony_ci /* Ensure the appropriate LEDs are enabled */ 21488c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, HW_CFG, ®); 21498c2ecf20Sopenharmony_ci reg &= ~(HW_CFG_LED0_EN_ | 21508c2ecf20Sopenharmony_ci HW_CFG_LED1_EN_ | 21518c2ecf20Sopenharmony_ci HW_CFG_LED2_EN_ | 21528c2ecf20Sopenharmony_ci HW_CFG_LED3_EN_); 21538c2ecf20Sopenharmony_ci reg |= (len > 0) * HW_CFG_LED0_EN_ | 21548c2ecf20Sopenharmony_ci (len > 1) * HW_CFG_LED1_EN_ | 21558c2ecf20Sopenharmony_ci (len > 2) * HW_CFG_LED2_EN_ | 21568c2ecf20Sopenharmony_ci (len > 3) * HW_CFG_LED3_EN_; 21578c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, HW_CFG, reg); 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci genphy_config_aneg(phydev); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci dev->fc_autoneg = phydev->autoneg; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci return 0; 21668c2ecf20Sopenharmony_ci} 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_cistatic int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size) 21698c2ecf20Sopenharmony_ci{ 21708c2ecf20Sopenharmony_ci u32 buf; 21718c2ecf20Sopenharmony_ci bool rxenabled; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, MAC_RX, &buf); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci rxenabled = ((buf & MAC_RX_RXEN_) != 0); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci if (rxenabled) { 21788c2ecf20Sopenharmony_ci buf &= ~MAC_RX_RXEN_; 21798c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_RX, buf); 21808c2ecf20Sopenharmony_ci } 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* add 4 to size for FCS */ 21838c2ecf20Sopenharmony_ci buf &= ~MAC_RX_MAX_SIZE_MASK_; 21848c2ecf20Sopenharmony_ci buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT_) & MAC_RX_MAX_SIZE_MASK_); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_RX, buf); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci if (rxenabled) { 21898c2ecf20Sopenharmony_ci buf |= MAC_RX_RXEN_; 21908c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_RX, buf); 21918c2ecf20Sopenharmony_ci } 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci return 0; 21948c2ecf20Sopenharmony_ci} 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_cistatic int unlink_urbs(struct lan78xx_net *dev, struct sk_buff_head *q) 21978c2ecf20Sopenharmony_ci{ 21988c2ecf20Sopenharmony_ci struct sk_buff *skb; 21998c2ecf20Sopenharmony_ci unsigned long flags; 22008c2ecf20Sopenharmony_ci int count = 0; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 22038c2ecf20Sopenharmony_ci while (!skb_queue_empty(q)) { 22048c2ecf20Sopenharmony_ci struct skb_data *entry; 22058c2ecf20Sopenharmony_ci struct urb *urb; 22068c2ecf20Sopenharmony_ci int ret; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci skb_queue_walk(q, skb) { 22098c2ecf20Sopenharmony_ci entry = (struct skb_data *)skb->cb; 22108c2ecf20Sopenharmony_ci if (entry->state != unlink_start) 22118c2ecf20Sopenharmony_ci goto found; 22128c2ecf20Sopenharmony_ci } 22138c2ecf20Sopenharmony_ci break; 22148c2ecf20Sopenharmony_cifound: 22158c2ecf20Sopenharmony_ci entry->state = unlink_start; 22168c2ecf20Sopenharmony_ci urb = entry->urb; 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci /* Get reference count of the URB to avoid it to be 22198c2ecf20Sopenharmony_ci * freed during usb_unlink_urb, which may trigger 22208c2ecf20Sopenharmony_ci * use-after-free problem inside usb_unlink_urb since 22218c2ecf20Sopenharmony_ci * usb_unlink_urb is always racing with .complete 22228c2ecf20Sopenharmony_ci * handler(include defer_bh). 22238c2ecf20Sopenharmony_ci */ 22248c2ecf20Sopenharmony_ci usb_get_urb(urb); 22258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 22268c2ecf20Sopenharmony_ci /* during some PM-driven resume scenarios, 22278c2ecf20Sopenharmony_ci * these (async) unlinks complete immediately 22288c2ecf20Sopenharmony_ci */ 22298c2ecf20Sopenharmony_ci ret = usb_unlink_urb(urb); 22308c2ecf20Sopenharmony_ci if (ret != -EINPROGRESS && ret != 0) 22318c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "unlink urb err, %d\n", ret); 22328c2ecf20Sopenharmony_ci else 22338c2ecf20Sopenharmony_ci count++; 22348c2ecf20Sopenharmony_ci usb_put_urb(urb); 22358c2ecf20Sopenharmony_ci spin_lock_irqsave(&q->lock, flags); 22368c2ecf20Sopenharmony_ci } 22378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&q->lock, flags); 22388c2ecf20Sopenharmony_ci return count; 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_cistatic int lan78xx_change_mtu(struct net_device *netdev, int new_mtu) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 22448c2ecf20Sopenharmony_ci int ll_mtu = new_mtu + netdev->hard_header_len; 22458c2ecf20Sopenharmony_ci int old_hard_mtu = dev->hard_mtu; 22468c2ecf20Sopenharmony_ci int old_rx_urb_size = dev->rx_urb_size; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci /* no second zero-length packet read wanted after mtu-sized packets */ 22498c2ecf20Sopenharmony_ci if ((ll_mtu % dev->maxpacket) == 0) 22508c2ecf20Sopenharmony_ci return -EDOM; 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci lan78xx_set_rx_max_frame_length(dev, new_mtu + VLAN_ETH_HLEN); 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci netdev->mtu = new_mtu; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci dev->hard_mtu = netdev->mtu + netdev->hard_header_len; 22578c2ecf20Sopenharmony_ci if (dev->rx_urb_size == old_hard_mtu) { 22588c2ecf20Sopenharmony_ci dev->rx_urb_size = dev->hard_mtu; 22598c2ecf20Sopenharmony_ci if (dev->rx_urb_size > old_rx_urb_size) { 22608c2ecf20Sopenharmony_ci if (netif_running(dev->net)) { 22618c2ecf20Sopenharmony_ci unlink_urbs(dev, &dev->rxq); 22628c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 22638c2ecf20Sopenharmony_ci } 22648c2ecf20Sopenharmony_ci } 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci return 0; 22688c2ecf20Sopenharmony_ci} 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_cistatic int lan78xx_set_mac_addr(struct net_device *netdev, void *p) 22718c2ecf20Sopenharmony_ci{ 22728c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 22738c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 22748c2ecf20Sopenharmony_ci u32 addr_lo, addr_hi; 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (netif_running(netdev)) 22778c2ecf20Sopenharmony_ci return -EBUSY; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 22808c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci ether_addr_copy(netdev->dev_addr, addr->sa_data); 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci addr_lo = netdev->dev_addr[0] | 22858c2ecf20Sopenharmony_ci netdev->dev_addr[1] << 8 | 22868c2ecf20Sopenharmony_ci netdev->dev_addr[2] << 16 | 22878c2ecf20Sopenharmony_ci netdev->dev_addr[3] << 24; 22888c2ecf20Sopenharmony_ci addr_hi = netdev->dev_addr[4] | 22898c2ecf20Sopenharmony_ci netdev->dev_addr[5] << 8; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RX_ADDRL, addr_lo); 22928c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RX_ADDRH, addr_hi); 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci /* Added to support MAC address changes */ 22958c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_LO(0), addr_lo); 22968c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci return 0; 22998c2ecf20Sopenharmony_ci} 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci/* Enable or disable Rx checksum offload engine */ 23028c2ecf20Sopenharmony_cistatic int lan78xx_set_features(struct net_device *netdev, 23038c2ecf20Sopenharmony_ci netdev_features_t features) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 23068c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 23078c2ecf20Sopenharmony_ci unsigned long flags; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci if (features & NETIF_F_RXCSUM) { 23128c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_TCPUDP_COE_ | RFE_CTL_IP_COE_; 23138c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_ICMP_COE_ | RFE_CTL_IGMP_COE_; 23148c2ecf20Sopenharmony_ci } else { 23158c2ecf20Sopenharmony_ci pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_COE_ | RFE_CTL_IP_COE_); 23168c2ecf20Sopenharmony_ci pdata->rfe_ctl &= ~(RFE_CTL_ICMP_COE_ | RFE_CTL_IGMP_COE_); 23178c2ecf20Sopenharmony_ci } 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 23208c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_VLAN_STRIP_; 23218c2ecf20Sopenharmony_ci else 23228c2ecf20Sopenharmony_ci pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_FILTER) 23258c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_; 23268c2ecf20Sopenharmony_ci else 23278c2ecf20Sopenharmony_ci pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci return 0; 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_cistatic void lan78xx_deferred_vlan_write(struct work_struct *param) 23378c2ecf20Sopenharmony_ci{ 23388c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = 23398c2ecf20Sopenharmony_ci container_of(param, struct lan78xx_priv, set_vlan); 23408c2ecf20Sopenharmony_ci struct lan78xx_net *dev = pdata->dev; 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0, 23438c2ecf20Sopenharmony_ci DP_SEL_VHF_VLAN_LEN, pdata->vlan_table); 23448c2ecf20Sopenharmony_ci} 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_cistatic int lan78xx_vlan_rx_add_vid(struct net_device *netdev, 23478c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 23488c2ecf20Sopenharmony_ci{ 23498c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 23508c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 23518c2ecf20Sopenharmony_ci u16 vid_bit_index; 23528c2ecf20Sopenharmony_ci u16 vid_dword_index; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci vid_dword_index = (vid >> 5) & 0x7F; 23558c2ecf20Sopenharmony_ci vid_bit_index = vid & 0x1F; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci pdata->vlan_table[vid_dword_index] |= (1 << vid_bit_index); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci /* defer register writes to a sleepable context */ 23608c2ecf20Sopenharmony_ci schedule_work(&pdata->set_vlan); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci return 0; 23638c2ecf20Sopenharmony_ci} 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_cistatic int lan78xx_vlan_rx_kill_vid(struct net_device *netdev, 23668c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 23678c2ecf20Sopenharmony_ci{ 23688c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(netdev); 23698c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 23708c2ecf20Sopenharmony_ci u16 vid_bit_index; 23718c2ecf20Sopenharmony_ci u16 vid_dword_index; 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci vid_dword_index = (vid >> 5) & 0x7F; 23748c2ecf20Sopenharmony_ci vid_bit_index = vid & 0x1F; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci pdata->vlan_table[vid_dword_index] &= ~(1 << vid_bit_index); 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci /* defer register writes to a sleepable context */ 23798c2ecf20Sopenharmony_ci schedule_work(&pdata->set_vlan); 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci return 0; 23828c2ecf20Sopenharmony_ci} 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_cistatic void lan78xx_init_ltm(struct lan78xx_net *dev) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci int ret; 23878c2ecf20Sopenharmony_ci u32 buf; 23888c2ecf20Sopenharmony_ci u32 regs[6] = { 0 }; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG1, &buf); 23918c2ecf20Sopenharmony_ci if (buf & USB_CFG1_LTM_ENABLE_) { 23928c2ecf20Sopenharmony_ci u8 temp[2]; 23938c2ecf20Sopenharmony_ci /* Get values from EEPROM first */ 23948c2ecf20Sopenharmony_ci if (lan78xx_read_eeprom(dev, 0x3F, 2, temp) == 0) { 23958c2ecf20Sopenharmony_ci if (temp[0] == 24) { 23968c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_eeprom(dev, 23978c2ecf20Sopenharmony_ci temp[1] * 2, 23988c2ecf20Sopenharmony_ci 24, 23998c2ecf20Sopenharmony_ci (u8 *)regs); 24008c2ecf20Sopenharmony_ci if (ret < 0) 24018c2ecf20Sopenharmony_ci return; 24028c2ecf20Sopenharmony_ci } 24038c2ecf20Sopenharmony_ci } else if (lan78xx_read_otp(dev, 0x3F, 2, temp) == 0) { 24048c2ecf20Sopenharmony_ci if (temp[0] == 24) { 24058c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_otp(dev, 24068c2ecf20Sopenharmony_ci temp[1] * 2, 24078c2ecf20Sopenharmony_ci 24, 24088c2ecf20Sopenharmony_ci (u8 *)regs); 24098c2ecf20Sopenharmony_ci if (ret < 0) 24108c2ecf20Sopenharmony_ci return; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci } 24138c2ecf20Sopenharmony_ci } 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, LTM_BELT_IDLE0, regs[0]); 24168c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, LTM_BELT_IDLE1, regs[1]); 24178c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, LTM_BELT_ACT0, regs[2]); 24188c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, LTM_BELT_ACT1, regs[3]); 24198c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, LTM_INACTIVE0, regs[4]); 24208c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, LTM_INACTIVE1, regs[5]); 24218c2ecf20Sopenharmony_ci} 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_cistatic int lan78xx_reset(struct lan78xx_net *dev) 24248c2ecf20Sopenharmony_ci{ 24258c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 24268c2ecf20Sopenharmony_ci u32 buf; 24278c2ecf20Sopenharmony_ci int ret = 0; 24288c2ecf20Sopenharmony_ci unsigned long timeout; 24298c2ecf20Sopenharmony_ci u8 sig; 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, HW_CFG, &buf); 24328c2ecf20Sopenharmony_ci buf |= HW_CFG_LRST_; 24338c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, buf); 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 24368c2ecf20Sopenharmony_ci do { 24378c2ecf20Sopenharmony_ci mdelay(1); 24388c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, HW_CFG, &buf); 24398c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 24408c2ecf20Sopenharmony_ci netdev_warn(dev->net, 24418c2ecf20Sopenharmony_ci "timeout on completion of LiteReset"); 24428c2ecf20Sopenharmony_ci return -EIO; 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci } while (buf & HW_CFG_LRST_); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci lan78xx_init_mac_address(dev); 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci /* save DEVID for later usage */ 24498c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, ID_REV, &buf); 24508c2ecf20Sopenharmony_ci dev->chipid = (buf & ID_REV_CHIP_ID_MASK_) >> 16; 24518c2ecf20Sopenharmony_ci dev->chiprev = buf & ID_REV_CHIP_REV_MASK_; 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci /* Respond to the IN token with a NAK */ 24548c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG0, &buf); 24558c2ecf20Sopenharmony_ci buf |= USB_CFG_BIR_; 24568c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, USB_CFG0, buf); 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci /* Init LTM */ 24598c2ecf20Sopenharmony_ci lan78xx_init_ltm(dev); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci if (dev->udev->speed == USB_SPEED_SUPER) { 24628c2ecf20Sopenharmony_ci buf = DEFAULT_BURST_CAP_SIZE / SS_USB_PKT_SIZE; 24638c2ecf20Sopenharmony_ci dev->rx_urb_size = DEFAULT_BURST_CAP_SIZE; 24648c2ecf20Sopenharmony_ci dev->rx_qlen = 4; 24658c2ecf20Sopenharmony_ci dev->tx_qlen = 4; 24668c2ecf20Sopenharmony_ci } else if (dev->udev->speed == USB_SPEED_HIGH) { 24678c2ecf20Sopenharmony_ci buf = DEFAULT_BURST_CAP_SIZE / HS_USB_PKT_SIZE; 24688c2ecf20Sopenharmony_ci dev->rx_urb_size = DEFAULT_BURST_CAP_SIZE; 24698c2ecf20Sopenharmony_ci dev->rx_qlen = RX_MAX_QUEUE_MEMORY / dev->rx_urb_size; 24708c2ecf20Sopenharmony_ci dev->tx_qlen = RX_MAX_QUEUE_MEMORY / dev->hard_mtu; 24718c2ecf20Sopenharmony_ci } else { 24728c2ecf20Sopenharmony_ci buf = DEFAULT_BURST_CAP_SIZE / FS_USB_PKT_SIZE; 24738c2ecf20Sopenharmony_ci dev->rx_urb_size = DEFAULT_BURST_CAP_SIZE; 24748c2ecf20Sopenharmony_ci dev->rx_qlen = 4; 24758c2ecf20Sopenharmony_ci dev->tx_qlen = 4; 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, BURST_CAP, buf); 24798c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, HW_CFG, &buf); 24828c2ecf20Sopenharmony_ci buf |= HW_CFG_MEF_; 24838c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, HW_CFG, buf); 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, USB_CFG0, &buf); 24868c2ecf20Sopenharmony_ci buf |= USB_CFG_BCE_; 24878c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, USB_CFG0, buf); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci /* set FIFO sizes */ 24908c2ecf20Sopenharmony_ci buf = (MAX_RX_FIFO_SIZE - 512) / 512; 24918c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, FCT_RX_FIFO_END, buf); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci buf = (MAX_TX_FIFO_SIZE - 512) / 512; 24948c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, FCT_TX_FIFO_END, buf); 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); 24978c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, FLOW, 0); 24988c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, FCT_FLOW, 0); 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci /* Don't need rfe_ctl_lock during initialisation */ 25018c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); 25028c2ecf20Sopenharmony_ci pdata->rfe_ctl |= RFE_CTL_BCAST_EN_ | RFE_CTL_DA_PERFECT_; 25038c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci /* Enable or disable checksum offload engines */ 25068c2ecf20Sopenharmony_ci lan78xx_set_features(dev->net, dev->net->features); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci lan78xx_set_multicast(dev->net); 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci /* reset PHY */ 25118c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, PMT_CTL, &buf); 25128c2ecf20Sopenharmony_ci buf |= PMT_CTL_PHY_RST_; 25138c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, PMT_CTL, buf); 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 25168c2ecf20Sopenharmony_ci do { 25178c2ecf20Sopenharmony_ci mdelay(1); 25188c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, PMT_CTL, &buf); 25198c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 25208c2ecf20Sopenharmony_ci netdev_warn(dev->net, "timeout waiting for PHY Reset"); 25218c2ecf20Sopenharmony_ci return -EIO; 25228c2ecf20Sopenharmony_ci } 25238c2ecf20Sopenharmony_ci } while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_)); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_CR, &buf); 25268c2ecf20Sopenharmony_ci /* LAN7801 only has RGMII mode */ 25278c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7801_) 25288c2ecf20Sopenharmony_ci buf &= ~MAC_CR_GMII_EN_; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci if (dev->chipid == ID_REV_CHIP_ID_7800_) { 25318c2ecf20Sopenharmony_ci ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig); 25328c2ecf20Sopenharmony_ci if (!ret && sig != EEPROM_INDICATOR) { 25338c2ecf20Sopenharmony_ci /* Implies there is no external eeprom. Set mac speed */ 25348c2ecf20Sopenharmony_ci netdev_info(dev->net, "No External EEPROM. Setting MAC Speed\n"); 25358c2ecf20Sopenharmony_ci buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci } 25388c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_CR, buf); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_TX, &buf); 25418c2ecf20Sopenharmony_ci buf |= MAC_TX_TXEN_; 25428c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_TX, buf); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, FCT_TX_CTL, &buf); 25458c2ecf20Sopenharmony_ci buf |= FCT_TX_CTL_EN_; 25468c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, FCT_TX_CTL, buf); 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci ret = lan78xx_set_rx_max_frame_length(dev, 25498c2ecf20Sopenharmony_ci dev->net->mtu + VLAN_ETH_HLEN); 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_RX, &buf); 25528c2ecf20Sopenharmony_ci buf |= MAC_RX_RXEN_; 25538c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_RX, buf); 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, FCT_RX_CTL, &buf); 25568c2ecf20Sopenharmony_ci buf |= FCT_RX_CTL_EN_; 25578c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, FCT_RX_CTL, buf); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci return 0; 25608c2ecf20Sopenharmony_ci} 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_cistatic void lan78xx_init_stats(struct lan78xx_net *dev) 25638c2ecf20Sopenharmony_ci{ 25648c2ecf20Sopenharmony_ci u32 *p; 25658c2ecf20Sopenharmony_ci int i; 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci /* initialize for stats update 25688c2ecf20Sopenharmony_ci * some counters are 20bits and some are 32bits 25698c2ecf20Sopenharmony_ci */ 25708c2ecf20Sopenharmony_ci p = (u32 *)&dev->stats.rollover_max; 25718c2ecf20Sopenharmony_ci for (i = 0; i < (sizeof(dev->stats.rollover_max) / (sizeof(u32))); i++) 25728c2ecf20Sopenharmony_ci p[i] = 0xFFFFF; 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci dev->stats.rollover_max.rx_unicast_byte_count = 0xFFFFFFFF; 25758c2ecf20Sopenharmony_ci dev->stats.rollover_max.rx_broadcast_byte_count = 0xFFFFFFFF; 25768c2ecf20Sopenharmony_ci dev->stats.rollover_max.rx_multicast_byte_count = 0xFFFFFFFF; 25778c2ecf20Sopenharmony_ci dev->stats.rollover_max.eee_rx_lpi_transitions = 0xFFFFFFFF; 25788c2ecf20Sopenharmony_ci dev->stats.rollover_max.eee_rx_lpi_time = 0xFFFFFFFF; 25798c2ecf20Sopenharmony_ci dev->stats.rollover_max.tx_unicast_byte_count = 0xFFFFFFFF; 25808c2ecf20Sopenharmony_ci dev->stats.rollover_max.tx_broadcast_byte_count = 0xFFFFFFFF; 25818c2ecf20Sopenharmony_ci dev->stats.rollover_max.tx_multicast_byte_count = 0xFFFFFFFF; 25828c2ecf20Sopenharmony_ci dev->stats.rollover_max.eee_tx_lpi_transitions = 0xFFFFFFFF; 25838c2ecf20Sopenharmony_ci dev->stats.rollover_max.eee_tx_lpi_time = 0xFFFFFFFF; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci set_bit(EVENT_STAT_UPDATE, &dev->flags); 25868c2ecf20Sopenharmony_ci} 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_cistatic int lan78xx_open(struct net_device *net) 25898c2ecf20Sopenharmony_ci{ 25908c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 25918c2ecf20Sopenharmony_ci int ret; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(dev->intf); 25948c2ecf20Sopenharmony_ci if (ret < 0) 25958c2ecf20Sopenharmony_ci goto out; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci phy_start(net->phydev); 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci /* for Link Check */ 26028c2ecf20Sopenharmony_ci if (dev->urb_intr) { 26038c2ecf20Sopenharmony_ci ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL); 26048c2ecf20Sopenharmony_ci if (ret < 0) { 26058c2ecf20Sopenharmony_ci netif_err(dev, ifup, dev->net, 26068c2ecf20Sopenharmony_ci "intr submit %d\n", ret); 26078c2ecf20Sopenharmony_ci goto done; 26088c2ecf20Sopenharmony_ci } 26098c2ecf20Sopenharmony_ci } 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci lan78xx_init_stats(dev); 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci set_bit(EVENT_DEV_OPEN, &dev->flags); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci netif_start_queue(net); 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci dev->link_on = false; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_LINK_RESET); 26208c2ecf20Sopenharmony_cidone: 26218c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ciout: 26248c2ecf20Sopenharmony_ci return ret; 26258c2ecf20Sopenharmony_ci} 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_cistatic void lan78xx_terminate_urbs(struct lan78xx_net *dev) 26288c2ecf20Sopenharmony_ci{ 26298c2ecf20Sopenharmony_ci DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup); 26308c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 26318c2ecf20Sopenharmony_ci int temp; 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci /* ensure there are no more active urbs */ 26348c2ecf20Sopenharmony_ci add_wait_queue(&unlink_wakeup, &wait); 26358c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 26368c2ecf20Sopenharmony_ci dev->wait = &unlink_wakeup; 26378c2ecf20Sopenharmony_ci temp = unlink_urbs(dev, &dev->txq) + unlink_urbs(dev, &dev->rxq); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci /* maybe wait for deletions to finish. */ 26408c2ecf20Sopenharmony_ci while (!skb_queue_empty(&dev->rxq) && 26418c2ecf20Sopenharmony_ci !skb_queue_empty(&dev->txq) && 26428c2ecf20Sopenharmony_ci !skb_queue_empty(&dev->done)) { 26438c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS)); 26448c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 26458c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, 26468c2ecf20Sopenharmony_ci "waited for %d urb completions\n", temp); 26478c2ecf20Sopenharmony_ci } 26488c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 26498c2ecf20Sopenharmony_ci dev->wait = NULL; 26508c2ecf20Sopenharmony_ci remove_wait_queue(&unlink_wakeup, &wait); 26518c2ecf20Sopenharmony_ci} 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_cistatic int lan78xx_stop(struct net_device *net) 26548c2ecf20Sopenharmony_ci{ 26558c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci if (timer_pending(&dev->stat_monitor)) 26588c2ecf20Sopenharmony_ci del_timer_sync(&dev->stat_monitor); 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci if (net->phydev) 26618c2ecf20Sopenharmony_ci phy_stop(net->phydev); 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci clear_bit(EVENT_DEV_OPEN, &dev->flags); 26648c2ecf20Sopenharmony_ci netif_stop_queue(net); 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci netif_info(dev, ifdown, dev->net, 26678c2ecf20Sopenharmony_ci "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n", 26688c2ecf20Sopenharmony_ci net->stats.rx_packets, net->stats.tx_packets, 26698c2ecf20Sopenharmony_ci net->stats.rx_errors, net->stats.tx_errors); 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci lan78xx_terminate_urbs(dev); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci usb_kill_urb(dev->urb_intr); 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci skb_queue_purge(&dev->rxq_pause); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci /* deferred work (task, timer, softirq) must also stop. 26788c2ecf20Sopenharmony_ci * can't flush_scheduled_work() until we drop rtnl (later), 26798c2ecf20Sopenharmony_ci * else workers could deadlock; so make workers a NOP. 26808c2ecf20Sopenharmony_ci */ 26818c2ecf20Sopenharmony_ci dev->flags = 0; 26828c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&dev->wq); 26838c2ecf20Sopenharmony_ci tasklet_kill(&dev->bh); 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci return 0; 26888c2ecf20Sopenharmony_ci} 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_cistatic struct sk_buff *lan78xx_tx_prep(struct lan78xx_net *dev, 26918c2ecf20Sopenharmony_ci struct sk_buff *skb, gfp_t flags) 26928c2ecf20Sopenharmony_ci{ 26938c2ecf20Sopenharmony_ci u32 tx_cmd_a, tx_cmd_b; 26948c2ecf20Sopenharmony_ci void *ptr; 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci if (skb_cow_head(skb, TX_OVERHEAD)) { 26978c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 26988c2ecf20Sopenharmony_ci return NULL; 26998c2ecf20Sopenharmony_ci } 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci if (skb_linearize(skb)) { 27028c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 27038c2ecf20Sopenharmony_ci return NULL; 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN_MASK_) | TX_CMD_A_FCS_; 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 27098c2ecf20Sopenharmony_ci tx_cmd_a |= TX_CMD_A_IPE_ | TX_CMD_A_TPE_; 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci tx_cmd_b = 0; 27128c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 27138c2ecf20Sopenharmony_ci u16 mss = max(skb_shinfo(skb)->gso_size, TX_CMD_B_MSS_MIN_); 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT_) & TX_CMD_B_MSS_MASK_; 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci tx_cmd_a |= TX_CMD_A_LSO_; 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 27218c2ecf20Sopenharmony_ci tx_cmd_a |= TX_CMD_A_IVTG_; 27228c2ecf20Sopenharmony_ci tx_cmd_b |= skb_vlan_tag_get(skb) & TX_CMD_B_VTAG_MASK_; 27238c2ecf20Sopenharmony_ci } 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci ptr = skb_push(skb, 8); 27268c2ecf20Sopenharmony_ci put_unaligned_le32(tx_cmd_a, ptr); 27278c2ecf20Sopenharmony_ci put_unaligned_le32(tx_cmd_b, ptr + 4); 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci return skb; 27308c2ecf20Sopenharmony_ci} 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_cistatic enum skb_state defer_bh(struct lan78xx_net *dev, struct sk_buff *skb, 27338c2ecf20Sopenharmony_ci struct sk_buff_head *list, enum skb_state state) 27348c2ecf20Sopenharmony_ci{ 27358c2ecf20Sopenharmony_ci unsigned long flags; 27368c2ecf20Sopenharmony_ci enum skb_state old_state; 27378c2ecf20Sopenharmony_ci struct skb_data *entry = (struct skb_data *)skb->cb; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci spin_lock_irqsave(&list->lock, flags); 27408c2ecf20Sopenharmony_ci old_state = entry->state; 27418c2ecf20Sopenharmony_ci entry->state = state; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci __skb_unlink(skb, list); 27448c2ecf20Sopenharmony_ci spin_unlock(&list->lock); 27458c2ecf20Sopenharmony_ci spin_lock(&dev->done.lock); 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci __skb_queue_tail(&dev->done, skb); 27488c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->done) == 1) 27498c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 27508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->done.lock, flags); 27518c2ecf20Sopenharmony_ci 27528c2ecf20Sopenharmony_ci return old_state; 27538c2ecf20Sopenharmony_ci} 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_cistatic void tx_complete(struct urb *urb) 27568c2ecf20Sopenharmony_ci{ 27578c2ecf20Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *)urb->context; 27588c2ecf20Sopenharmony_ci struct skb_data *entry = (struct skb_data *)skb->cb; 27598c2ecf20Sopenharmony_ci struct lan78xx_net *dev = entry->dev; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci if (urb->status == 0) { 27628c2ecf20Sopenharmony_ci dev->net->stats.tx_packets += entry->num_of_packet; 27638c2ecf20Sopenharmony_ci dev->net->stats.tx_bytes += entry->length; 27648c2ecf20Sopenharmony_ci } else { 27658c2ecf20Sopenharmony_ci dev->net->stats.tx_errors++; 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci switch (urb->status) { 27688c2ecf20Sopenharmony_ci case -EPIPE: 27698c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_TX_HALT); 27708c2ecf20Sopenharmony_ci break; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci /* software-driven interface shutdown */ 27738c2ecf20Sopenharmony_ci case -ECONNRESET: 27748c2ecf20Sopenharmony_ci case -ESHUTDOWN: 27758c2ecf20Sopenharmony_ci break; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci case -EPROTO: 27788c2ecf20Sopenharmony_ci case -ETIME: 27798c2ecf20Sopenharmony_ci case -EILSEQ: 27808c2ecf20Sopenharmony_ci netif_stop_queue(dev->net); 27818c2ecf20Sopenharmony_ci break; 27828c2ecf20Sopenharmony_ci default: 27838c2ecf20Sopenharmony_ci netif_dbg(dev, tx_err, dev->net, 27848c2ecf20Sopenharmony_ci "tx err %d\n", entry->urb->status); 27858c2ecf20Sopenharmony_ci break; 27868c2ecf20Sopenharmony_ci } 27878c2ecf20Sopenharmony_ci } 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci usb_autopm_put_interface_async(dev->intf); 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci defer_bh(dev, skb, &dev->txq, tx_done); 27928c2ecf20Sopenharmony_ci} 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_cistatic void lan78xx_queue_skb(struct sk_buff_head *list, 27958c2ecf20Sopenharmony_ci struct sk_buff *newsk, enum skb_state state) 27968c2ecf20Sopenharmony_ci{ 27978c2ecf20Sopenharmony_ci struct skb_data *entry = (struct skb_data *)newsk->cb; 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci __skb_queue_tail(list, newsk); 28008c2ecf20Sopenharmony_ci entry->state = state; 28018c2ecf20Sopenharmony_ci} 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_cistatic netdev_tx_t 28048c2ecf20Sopenharmony_cilan78xx_start_xmit(struct sk_buff *skb, struct net_device *net) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 28078c2ecf20Sopenharmony_ci struct sk_buff *skb2 = NULL; 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci if (skb) { 28108c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 28118c2ecf20Sopenharmony_ci skb2 = lan78xx_tx_prep(dev, skb, GFP_ATOMIC); 28128c2ecf20Sopenharmony_ci } 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci if (skb2) { 28158c2ecf20Sopenharmony_ci skb_queue_tail(&dev->txq_pend, skb2); 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci /* throttle TX patch at slower than SUPER SPEED USB */ 28188c2ecf20Sopenharmony_ci if ((dev->udev->speed < USB_SPEED_SUPER) && 28198c2ecf20Sopenharmony_ci (skb_queue_len(&dev->txq_pend) > 10)) 28208c2ecf20Sopenharmony_ci netif_stop_queue(net); 28218c2ecf20Sopenharmony_ci } else { 28228c2ecf20Sopenharmony_ci netif_dbg(dev, tx_err, dev->net, 28238c2ecf20Sopenharmony_ci "lan78xx_tx_prep return NULL\n"); 28248c2ecf20Sopenharmony_ci dev->net->stats.tx_errors++; 28258c2ecf20Sopenharmony_ci dev->net->stats.tx_dropped++; 28268c2ecf20Sopenharmony_ci } 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 28318c2ecf20Sopenharmony_ci} 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_cistatic int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) 28348c2ecf20Sopenharmony_ci{ 28358c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = NULL; 28368c2ecf20Sopenharmony_ci int ret; 28378c2ecf20Sopenharmony_ci int i; 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci dev->data[0] = (unsigned long)kzalloc(sizeof(*pdata), GFP_KERNEL); 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci pdata = (struct lan78xx_priv *)(dev->data[0]); 28428c2ecf20Sopenharmony_ci if (!pdata) { 28438c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Unable to allocate lan78xx_priv"); 28448c2ecf20Sopenharmony_ci return -ENOMEM; 28458c2ecf20Sopenharmony_ci } 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci pdata->dev = dev; 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci spin_lock_init(&pdata->rfe_ctl_lock); 28508c2ecf20Sopenharmony_ci mutex_init(&pdata->dataport_mutex); 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci INIT_WORK(&pdata->set_multicast, lan78xx_deferred_multicast_write); 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci for (i = 0; i < DP_SEL_VHF_VLAN_LEN; i++) 28558c2ecf20Sopenharmony_ci pdata->vlan_table[i] = 0; 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci INIT_WORK(&pdata->set_vlan, lan78xx_deferred_vlan_write); 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci dev->net->features = 0; 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci if (DEFAULT_TX_CSUM_ENABLE) 28628c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_HW_CSUM; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (DEFAULT_RX_CSUM_ENABLE) 28658c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_RXCSUM; 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci if (DEFAULT_TSO_CSUM_ENABLE) 28688c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG; 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci if (DEFAULT_VLAN_RX_OFFLOAD) 28718c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_HW_VLAN_CTAG_RX; 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci if (DEFAULT_VLAN_FILTER_ENABLE) 28748c2ecf20Sopenharmony_ci dev->net->features |= NETIF_F_HW_VLAN_CTAG_FILTER; 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci dev->net->hw_features = dev->net->features; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci ret = lan78xx_setup_irq_domain(dev); 28798c2ecf20Sopenharmony_ci if (ret < 0) { 28808c2ecf20Sopenharmony_ci netdev_warn(dev->net, 28818c2ecf20Sopenharmony_ci "lan78xx_setup_irq_domain() failed : %d", ret); 28828c2ecf20Sopenharmony_ci goto out1; 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci dev->net->hard_header_len += TX_OVERHEAD; 28868c2ecf20Sopenharmony_ci dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci /* Init all registers */ 28898c2ecf20Sopenharmony_ci ret = lan78xx_reset(dev); 28908c2ecf20Sopenharmony_ci if (ret) { 28918c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Registers INIT FAILED...."); 28928c2ecf20Sopenharmony_ci goto out2; 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci ret = lan78xx_mdio_init(dev); 28968c2ecf20Sopenharmony_ci if (ret) { 28978c2ecf20Sopenharmony_ci netdev_warn(dev->net, "MDIO INIT FAILED....."); 28988c2ecf20Sopenharmony_ci goto out2; 28998c2ecf20Sopenharmony_ci } 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci dev->net->flags |= IFF_MULTICAST; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci pdata->wol = WAKE_MAGIC; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci return ret; 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ciout2: 29088c2ecf20Sopenharmony_ci lan78xx_remove_irq_domain(dev); 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ciout1: 29118c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Bind routine FAILED"); 29128c2ecf20Sopenharmony_ci cancel_work_sync(&pdata->set_multicast); 29138c2ecf20Sopenharmony_ci cancel_work_sync(&pdata->set_vlan); 29148c2ecf20Sopenharmony_ci kfree(pdata); 29158c2ecf20Sopenharmony_ci return ret; 29168c2ecf20Sopenharmony_ci} 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_cistatic void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf) 29198c2ecf20Sopenharmony_ci{ 29208c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci lan78xx_remove_irq_domain(dev); 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci lan78xx_remove_mdio(dev); 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci if (pdata) { 29278c2ecf20Sopenharmony_ci cancel_work_sync(&pdata->set_multicast); 29288c2ecf20Sopenharmony_ci cancel_work_sync(&pdata->set_vlan); 29298c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, "free pdata"); 29308c2ecf20Sopenharmony_ci kfree(pdata); 29318c2ecf20Sopenharmony_ci pdata = NULL; 29328c2ecf20Sopenharmony_ci dev->data[0] = 0; 29338c2ecf20Sopenharmony_ci } 29348c2ecf20Sopenharmony_ci} 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_cistatic void lan78xx_rx_csum_offload(struct lan78xx_net *dev, 29378c2ecf20Sopenharmony_ci struct sk_buff *skb, 29388c2ecf20Sopenharmony_ci u32 rx_cmd_a, u32 rx_cmd_b) 29398c2ecf20Sopenharmony_ci{ 29408c2ecf20Sopenharmony_ci /* HW Checksum offload appears to be flawed if used when not stripping 29418c2ecf20Sopenharmony_ci * VLAN headers. Drop back to S/W checksums under these conditions. 29428c2ecf20Sopenharmony_ci */ 29438c2ecf20Sopenharmony_ci if (!(dev->net->features & NETIF_F_RXCSUM) || 29448c2ecf20Sopenharmony_ci unlikely(rx_cmd_a & RX_CMD_A_ICSM_) || 29458c2ecf20Sopenharmony_ci ((rx_cmd_a & RX_CMD_A_FVTG_) && 29468c2ecf20Sopenharmony_ci !(dev->net->features & NETIF_F_HW_VLAN_CTAG_RX))) { 29478c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 29488c2ecf20Sopenharmony_ci } else { 29498c2ecf20Sopenharmony_ci skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT_)); 29508c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 29518c2ecf20Sopenharmony_ci } 29528c2ecf20Sopenharmony_ci} 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_cistatic void lan78xx_rx_vlan_offload(struct lan78xx_net *dev, 29558c2ecf20Sopenharmony_ci struct sk_buff *skb, 29568c2ecf20Sopenharmony_ci u32 rx_cmd_a, u32 rx_cmd_b) 29578c2ecf20Sopenharmony_ci{ 29588c2ecf20Sopenharmony_ci if ((dev->net->features & NETIF_F_HW_VLAN_CTAG_RX) && 29598c2ecf20Sopenharmony_ci (rx_cmd_a & RX_CMD_A_FVTG_)) 29608c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 29618c2ecf20Sopenharmony_ci (rx_cmd_b & 0xffff)); 29628c2ecf20Sopenharmony_ci} 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_cistatic void lan78xx_skb_return(struct lan78xx_net *dev, struct sk_buff *skb) 29658c2ecf20Sopenharmony_ci{ 29668c2ecf20Sopenharmony_ci int status; 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { 29698c2ecf20Sopenharmony_ci skb_queue_tail(&dev->rxq_pause, skb); 29708c2ecf20Sopenharmony_ci return; 29718c2ecf20Sopenharmony_ci } 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci dev->net->stats.rx_packets++; 29748c2ecf20Sopenharmony_ci dev->net->stats.rx_bytes += skb->len; 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev->net); 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", 29798c2ecf20Sopenharmony_ci skb->len + sizeof(struct ethhdr), skb->protocol); 29808c2ecf20Sopenharmony_ci memset(skb->cb, 0, sizeof(struct skb_data)); 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci if (skb_defer_rx_timestamp(skb)) 29838c2ecf20Sopenharmony_ci return; 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci status = netif_rx(skb); 29868c2ecf20Sopenharmony_ci if (status != NET_RX_SUCCESS) 29878c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 29888c2ecf20Sopenharmony_ci "netif_rx status %d\n", status); 29898c2ecf20Sopenharmony_ci} 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_cistatic int lan78xx_rx(struct lan78xx_net *dev, struct sk_buff *skb) 29928c2ecf20Sopenharmony_ci{ 29938c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 29948c2ecf20Sopenharmony_ci return 0; 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci while (skb->len > 0) { 29978c2ecf20Sopenharmony_ci u32 rx_cmd_a, rx_cmd_b, align_count, size; 29988c2ecf20Sopenharmony_ci u16 rx_cmd_c; 29998c2ecf20Sopenharmony_ci struct sk_buff *skb2; 30008c2ecf20Sopenharmony_ci unsigned char *packet; 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci rx_cmd_a = get_unaligned_le32(skb->data); 30038c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(rx_cmd_a)); 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci rx_cmd_b = get_unaligned_le32(skb->data); 30068c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(rx_cmd_b)); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci rx_cmd_c = get_unaligned_le16(skb->data); 30098c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(rx_cmd_c)); 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_ci packet = skb->data; 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci /* get the packet length */ 30148c2ecf20Sopenharmony_ci size = (rx_cmd_a & RX_CMD_A_LEN_MASK_); 30158c2ecf20Sopenharmony_ci align_count = (4 - ((size + RXW_PADDING) % 4)) % 4; 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci if (unlikely(rx_cmd_a & RX_CMD_A_RED_)) { 30188c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 30198c2ecf20Sopenharmony_ci "Error rx_cmd_a=0x%08x", rx_cmd_a); 30208c2ecf20Sopenharmony_ci } else { 30218c2ecf20Sopenharmony_ci /* last frame in this batch */ 30228c2ecf20Sopenharmony_ci if (skb->len == size) { 30238c2ecf20Sopenharmony_ci lan78xx_rx_csum_offload(dev, skb, 30248c2ecf20Sopenharmony_ci rx_cmd_a, rx_cmd_b); 30258c2ecf20Sopenharmony_ci lan78xx_rx_vlan_offload(dev, skb, 30268c2ecf20Sopenharmony_ci rx_cmd_a, rx_cmd_b); 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - 4); /* remove fcs */ 30298c2ecf20Sopenharmony_ci skb->truesize = size + sizeof(struct sk_buff); 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci return 1; 30328c2ecf20Sopenharmony_ci } 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 30358c2ecf20Sopenharmony_ci if (unlikely(!skb2)) { 30368c2ecf20Sopenharmony_ci netdev_warn(dev->net, "Error allocating skb"); 30378c2ecf20Sopenharmony_ci return 0; 30388c2ecf20Sopenharmony_ci } 30398c2ecf20Sopenharmony_ci 30408c2ecf20Sopenharmony_ci skb2->len = size; 30418c2ecf20Sopenharmony_ci skb2->data = packet; 30428c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb2, size); 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci lan78xx_rx_csum_offload(dev, skb2, rx_cmd_a, rx_cmd_b); 30458c2ecf20Sopenharmony_ci lan78xx_rx_vlan_offload(dev, skb2, rx_cmd_a, rx_cmd_b); 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci skb_trim(skb2, skb2->len - 4); /* remove fcs */ 30488c2ecf20Sopenharmony_ci skb2->truesize = size + sizeof(struct sk_buff); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci lan78xx_skb_return(dev, skb2); 30518c2ecf20Sopenharmony_ci } 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci skb_pull(skb, size); 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci /* padding bytes before the next frame starts */ 30568c2ecf20Sopenharmony_ci if (skb->len) 30578c2ecf20Sopenharmony_ci skb_pull(skb, align_count); 30588c2ecf20Sopenharmony_ci } 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci return 1; 30618c2ecf20Sopenharmony_ci} 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_cistatic inline void rx_process(struct lan78xx_net *dev, struct sk_buff *skb) 30648c2ecf20Sopenharmony_ci{ 30658c2ecf20Sopenharmony_ci if (!lan78xx_rx(dev, skb)) { 30668c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 30678c2ecf20Sopenharmony_ci goto done; 30688c2ecf20Sopenharmony_ci } 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci if (skb->len) { 30718c2ecf20Sopenharmony_ci lan78xx_skb_return(dev, skb); 30728c2ecf20Sopenharmony_ci return; 30738c2ecf20Sopenharmony_ci } 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, "drop\n"); 30768c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 30778c2ecf20Sopenharmony_cidone: 30788c2ecf20Sopenharmony_ci skb_queue_tail(&dev->done, skb); 30798c2ecf20Sopenharmony_ci} 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_cistatic void rx_complete(struct urb *urb); 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_cistatic int rx_submit(struct lan78xx_net *dev, struct urb *urb, gfp_t flags) 30848c2ecf20Sopenharmony_ci{ 30858c2ecf20Sopenharmony_ci struct sk_buff *skb; 30868c2ecf20Sopenharmony_ci struct skb_data *entry; 30878c2ecf20Sopenharmony_ci unsigned long lockflags; 30888c2ecf20Sopenharmony_ci size_t size = dev->rx_urb_size; 30898c2ecf20Sopenharmony_ci int ret = 0; 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev->net, size); 30928c2ecf20Sopenharmony_ci if (!skb) { 30938c2ecf20Sopenharmony_ci usb_free_urb(urb); 30948c2ecf20Sopenharmony_ci return -ENOMEM; 30958c2ecf20Sopenharmony_ci } 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci entry = (struct skb_data *)skb->cb; 30988c2ecf20Sopenharmony_ci entry->urb = urb; 30998c2ecf20Sopenharmony_ci entry->dev = dev; 31008c2ecf20Sopenharmony_ci entry->length = 0; 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, dev->udev, dev->pipe_in, 31038c2ecf20Sopenharmony_ci skb->data, size, rx_complete, skb); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->rxq.lock, lockflags); 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci if (netif_device_present(dev->net) && 31088c2ecf20Sopenharmony_ci netif_running(dev->net) && 31098c2ecf20Sopenharmony_ci !test_bit(EVENT_RX_HALT, &dev->flags) && 31108c2ecf20Sopenharmony_ci !test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { 31118c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 31128c2ecf20Sopenharmony_ci switch (ret) { 31138c2ecf20Sopenharmony_ci case 0: 31148c2ecf20Sopenharmony_ci lan78xx_queue_skb(&dev->rxq, skb, rx_start); 31158c2ecf20Sopenharmony_ci break; 31168c2ecf20Sopenharmony_ci case -EPIPE: 31178c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_RX_HALT); 31188c2ecf20Sopenharmony_ci break; 31198c2ecf20Sopenharmony_ci case -ENODEV: 31208c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, "device gone\n"); 31218c2ecf20Sopenharmony_ci netif_device_detach(dev->net); 31228c2ecf20Sopenharmony_ci break; 31238c2ecf20Sopenharmony_ci case -EHOSTUNREACH: 31248c2ecf20Sopenharmony_ci ret = -ENOLINK; 31258c2ecf20Sopenharmony_ci break; 31268c2ecf20Sopenharmony_ci default: 31278c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 31288c2ecf20Sopenharmony_ci "rx submit, %d\n", ret); 31298c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 31308c2ecf20Sopenharmony_ci } 31318c2ecf20Sopenharmony_ci } else { 31328c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, "rx: stopped\n"); 31338c2ecf20Sopenharmony_ci ret = -ENOLINK; 31348c2ecf20Sopenharmony_ci } 31358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->rxq.lock, lockflags); 31368c2ecf20Sopenharmony_ci if (ret) { 31378c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 31388c2ecf20Sopenharmony_ci usb_free_urb(urb); 31398c2ecf20Sopenharmony_ci } 31408c2ecf20Sopenharmony_ci return ret; 31418c2ecf20Sopenharmony_ci} 31428c2ecf20Sopenharmony_ci 31438c2ecf20Sopenharmony_cistatic void rx_complete(struct urb *urb) 31448c2ecf20Sopenharmony_ci{ 31458c2ecf20Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *)urb->context; 31468c2ecf20Sopenharmony_ci struct skb_data *entry = (struct skb_data *)skb->cb; 31478c2ecf20Sopenharmony_ci struct lan78xx_net *dev = entry->dev; 31488c2ecf20Sopenharmony_ci int urb_status = urb->status; 31498c2ecf20Sopenharmony_ci enum skb_state state; 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci skb_put(skb, urb->actual_length); 31528c2ecf20Sopenharmony_ci state = rx_done; 31538c2ecf20Sopenharmony_ci entry->urb = NULL; 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci switch (urb_status) { 31568c2ecf20Sopenharmony_ci case 0: 31578c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) { 31588c2ecf20Sopenharmony_ci state = rx_cleanup; 31598c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 31608c2ecf20Sopenharmony_ci dev->net->stats.rx_length_errors++; 31618c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 31628c2ecf20Sopenharmony_ci "rx length %d\n", skb->len); 31638c2ecf20Sopenharmony_ci } 31648c2ecf20Sopenharmony_ci usb_mark_last_busy(dev->udev); 31658c2ecf20Sopenharmony_ci break; 31668c2ecf20Sopenharmony_ci case -EPIPE: 31678c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 31688c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_RX_HALT); 31698c2ecf20Sopenharmony_ci fallthrough; 31708c2ecf20Sopenharmony_ci case -ECONNRESET: /* async unlink */ 31718c2ecf20Sopenharmony_ci case -ESHUTDOWN: /* hardware gone */ 31728c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, 31738c2ecf20Sopenharmony_ci "rx shutdown, code %d\n", urb_status); 31748c2ecf20Sopenharmony_ci state = rx_cleanup; 31758c2ecf20Sopenharmony_ci entry->urb = urb; 31768c2ecf20Sopenharmony_ci urb = NULL; 31778c2ecf20Sopenharmony_ci break; 31788c2ecf20Sopenharmony_ci case -EPROTO: 31798c2ecf20Sopenharmony_ci case -ETIME: 31808c2ecf20Sopenharmony_ci case -EILSEQ: 31818c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 31828c2ecf20Sopenharmony_ci state = rx_cleanup; 31838c2ecf20Sopenharmony_ci entry->urb = urb; 31848c2ecf20Sopenharmony_ci urb = NULL; 31858c2ecf20Sopenharmony_ci break; 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci /* data overrun ... flush fifo? */ 31888c2ecf20Sopenharmony_ci case -EOVERFLOW: 31898c2ecf20Sopenharmony_ci dev->net->stats.rx_over_errors++; 31908c2ecf20Sopenharmony_ci fallthrough; 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci default: 31938c2ecf20Sopenharmony_ci state = rx_cleanup; 31948c2ecf20Sopenharmony_ci dev->net->stats.rx_errors++; 31958c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status); 31968c2ecf20Sopenharmony_ci break; 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci state = defer_bh(dev, skb, &dev->rxq, state); 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci if (urb) { 32028c2ecf20Sopenharmony_ci if (netif_running(dev->net) && 32038c2ecf20Sopenharmony_ci !test_bit(EVENT_RX_HALT, &dev->flags) && 32048c2ecf20Sopenharmony_ci state != unlink_start) { 32058c2ecf20Sopenharmony_ci rx_submit(dev, urb, GFP_ATOMIC); 32068c2ecf20Sopenharmony_ci return; 32078c2ecf20Sopenharmony_ci } 32088c2ecf20Sopenharmony_ci usb_free_urb(urb); 32098c2ecf20Sopenharmony_ci } 32108c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n"); 32118c2ecf20Sopenharmony_ci} 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_cistatic void lan78xx_tx_bh(struct lan78xx_net *dev) 32148c2ecf20Sopenharmony_ci{ 32158c2ecf20Sopenharmony_ci int length; 32168c2ecf20Sopenharmony_ci struct urb *urb = NULL; 32178c2ecf20Sopenharmony_ci struct skb_data *entry; 32188c2ecf20Sopenharmony_ci unsigned long flags; 32198c2ecf20Sopenharmony_ci struct sk_buff_head *tqp = &dev->txq_pend; 32208c2ecf20Sopenharmony_ci struct sk_buff *skb, *skb2; 32218c2ecf20Sopenharmony_ci int ret; 32228c2ecf20Sopenharmony_ci int count, pos; 32238c2ecf20Sopenharmony_ci int skb_totallen, pkt_cnt; 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci skb_totallen = 0; 32268c2ecf20Sopenharmony_ci pkt_cnt = 0; 32278c2ecf20Sopenharmony_ci count = 0; 32288c2ecf20Sopenharmony_ci length = 0; 32298c2ecf20Sopenharmony_ci spin_lock_irqsave(&tqp->lock, flags); 32308c2ecf20Sopenharmony_ci skb_queue_walk(tqp, skb) { 32318c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 32328c2ecf20Sopenharmony_ci if (!skb_queue_is_first(tqp, skb)) { 32338c2ecf20Sopenharmony_ci /* handle previous packets first */ 32348c2ecf20Sopenharmony_ci break; 32358c2ecf20Sopenharmony_ci } 32368c2ecf20Sopenharmony_ci count = 1; 32378c2ecf20Sopenharmony_ci length = skb->len - TX_OVERHEAD; 32388c2ecf20Sopenharmony_ci __skb_unlink(skb, tqp); 32398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tqp->lock, flags); 32408c2ecf20Sopenharmony_ci goto gso_skb; 32418c2ecf20Sopenharmony_ci } 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci if ((skb_totallen + skb->len) > MAX_SINGLE_PACKET_SIZE) 32448c2ecf20Sopenharmony_ci break; 32458c2ecf20Sopenharmony_ci skb_totallen = skb->len + roundup(skb_totallen, sizeof(u32)); 32468c2ecf20Sopenharmony_ci pkt_cnt++; 32478c2ecf20Sopenharmony_ci } 32488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tqp->lock, flags); 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci /* copy to a single skb */ 32518c2ecf20Sopenharmony_ci skb = alloc_skb(skb_totallen, GFP_ATOMIC); 32528c2ecf20Sopenharmony_ci if (!skb) 32538c2ecf20Sopenharmony_ci goto drop; 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci skb_put(skb, skb_totallen); 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci for (count = pos = 0; count < pkt_cnt; count++) { 32588c2ecf20Sopenharmony_ci skb2 = skb_dequeue(tqp); 32598c2ecf20Sopenharmony_ci if (skb2) { 32608c2ecf20Sopenharmony_ci length += (skb2->len - TX_OVERHEAD); 32618c2ecf20Sopenharmony_ci memcpy(skb->data + pos, skb2->data, skb2->len); 32628c2ecf20Sopenharmony_ci pos += roundup(skb2->len, sizeof(u32)); 32638c2ecf20Sopenharmony_ci dev_kfree_skb(skb2); 32648c2ecf20Sopenharmony_ci } 32658c2ecf20Sopenharmony_ci } 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_cigso_skb: 32688c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 32698c2ecf20Sopenharmony_ci if (!urb) 32708c2ecf20Sopenharmony_ci goto drop; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci entry = (struct skb_data *)skb->cb; 32738c2ecf20Sopenharmony_ci entry->urb = urb; 32748c2ecf20Sopenharmony_ci entry->dev = dev; 32758c2ecf20Sopenharmony_ci entry->length = length; 32768c2ecf20Sopenharmony_ci entry->num_of_packet = count; 32778c2ecf20Sopenharmony_ci 32788c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->txq.lock, flags); 32798c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface_async(dev->intf); 32808c2ecf20Sopenharmony_ci if (ret < 0) { 32818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->txq.lock, flags); 32828c2ecf20Sopenharmony_ci goto drop; 32838c2ecf20Sopenharmony_ci } 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, dev->udev, dev->pipe_out, 32868c2ecf20Sopenharmony_ci skb->data, skb->len, tx_complete, skb); 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ci if (length % dev->maxpacket == 0) { 32898c2ecf20Sopenharmony_ci /* send USB_ZERO_PACKET */ 32908c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_ZERO_PACKET; 32918c2ecf20Sopenharmony_ci } 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 32948c2ecf20Sopenharmony_ci /* if this triggers the device is still a sleep */ 32958c2ecf20Sopenharmony_ci if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { 32968c2ecf20Sopenharmony_ci /* transmission will be done in resume */ 32978c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &dev->deferred); 32988c2ecf20Sopenharmony_ci /* no use to process more packets */ 32998c2ecf20Sopenharmony_ci netif_stop_queue(dev->net); 33008c2ecf20Sopenharmony_ci usb_put_urb(urb); 33018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->txq.lock, flags); 33028c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "Delaying transmission for resumption\n"); 33038c2ecf20Sopenharmony_ci return; 33048c2ecf20Sopenharmony_ci } 33058c2ecf20Sopenharmony_ci#endif 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 33088c2ecf20Sopenharmony_ci switch (ret) { 33098c2ecf20Sopenharmony_ci case 0: 33108c2ecf20Sopenharmony_ci netif_trans_update(dev->net); 33118c2ecf20Sopenharmony_ci lan78xx_queue_skb(&dev->txq, skb, tx_start); 33128c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->txq) >= dev->tx_qlen) 33138c2ecf20Sopenharmony_ci netif_stop_queue(dev->net); 33148c2ecf20Sopenharmony_ci break; 33158c2ecf20Sopenharmony_ci case -EPIPE: 33168c2ecf20Sopenharmony_ci netif_stop_queue(dev->net); 33178c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_TX_HALT); 33188c2ecf20Sopenharmony_ci usb_autopm_put_interface_async(dev->intf); 33198c2ecf20Sopenharmony_ci break; 33208c2ecf20Sopenharmony_ci default: 33218c2ecf20Sopenharmony_ci usb_autopm_put_interface_async(dev->intf); 33228c2ecf20Sopenharmony_ci netif_dbg(dev, tx_err, dev->net, 33238c2ecf20Sopenharmony_ci "tx: submit urb err %d\n", ret); 33248c2ecf20Sopenharmony_ci break; 33258c2ecf20Sopenharmony_ci } 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->txq.lock, flags); 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci if (ret) { 33308c2ecf20Sopenharmony_ci netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", ret); 33318c2ecf20Sopenharmony_cidrop: 33328c2ecf20Sopenharmony_ci dev->net->stats.tx_dropped++; 33338c2ecf20Sopenharmony_ci if (skb) 33348c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 33358c2ecf20Sopenharmony_ci usb_free_urb(urb); 33368c2ecf20Sopenharmony_ci } else 33378c2ecf20Sopenharmony_ci netif_dbg(dev, tx_queued, dev->net, 33388c2ecf20Sopenharmony_ci "> tx, len %d, type 0x%x\n", length, skb->protocol); 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_cistatic void lan78xx_rx_bh(struct lan78xx_net *dev) 33428c2ecf20Sopenharmony_ci{ 33438c2ecf20Sopenharmony_ci struct urb *urb; 33448c2ecf20Sopenharmony_ci int i; 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->rxq) < dev->rx_qlen) { 33478c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 33488c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->rxq) >= dev->rx_qlen) 33498c2ecf20Sopenharmony_ci break; 33508c2ecf20Sopenharmony_ci urb = usb_alloc_urb(0, GFP_ATOMIC); 33518c2ecf20Sopenharmony_ci if (urb) 33528c2ecf20Sopenharmony_ci if (rx_submit(dev, urb, GFP_ATOMIC) == -ENOLINK) 33538c2ecf20Sopenharmony_ci return; 33548c2ecf20Sopenharmony_ci } 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->rxq) < dev->rx_qlen) 33578c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 33588c2ecf20Sopenharmony_ci } 33598c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->txq) < dev->tx_qlen) 33608c2ecf20Sopenharmony_ci netif_wake_queue(dev->net); 33618c2ecf20Sopenharmony_ci} 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_cistatic void lan78xx_bh(unsigned long param) 33648c2ecf20Sopenharmony_ci{ 33658c2ecf20Sopenharmony_ci struct lan78xx_net *dev = (struct lan78xx_net *)param; 33668c2ecf20Sopenharmony_ci struct sk_buff *skb; 33678c2ecf20Sopenharmony_ci struct skb_data *entry; 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&dev->done))) { 33708c2ecf20Sopenharmony_ci entry = (struct skb_data *)(skb->cb); 33718c2ecf20Sopenharmony_ci switch (entry->state) { 33728c2ecf20Sopenharmony_ci case rx_done: 33738c2ecf20Sopenharmony_ci entry->state = rx_cleanup; 33748c2ecf20Sopenharmony_ci rx_process(dev, skb); 33758c2ecf20Sopenharmony_ci continue; 33768c2ecf20Sopenharmony_ci case tx_done: 33778c2ecf20Sopenharmony_ci usb_free_urb(entry->urb); 33788c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 33798c2ecf20Sopenharmony_ci continue; 33808c2ecf20Sopenharmony_ci case rx_cleanup: 33818c2ecf20Sopenharmony_ci usb_free_urb(entry->urb); 33828c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 33838c2ecf20Sopenharmony_ci continue; 33848c2ecf20Sopenharmony_ci default: 33858c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "skb state %d\n", entry->state); 33868c2ecf20Sopenharmony_ci return; 33878c2ecf20Sopenharmony_ci } 33888c2ecf20Sopenharmony_ci } 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci if (netif_device_present(dev->net) && netif_running(dev->net)) { 33918c2ecf20Sopenharmony_ci /* reset update timer delta */ 33928c2ecf20Sopenharmony_ci if (timer_pending(&dev->stat_monitor) && (dev->delta != 1)) { 33938c2ecf20Sopenharmony_ci dev->delta = 1; 33948c2ecf20Sopenharmony_ci mod_timer(&dev->stat_monitor, 33958c2ecf20Sopenharmony_ci jiffies + STAT_UPDATE_TIMER); 33968c2ecf20Sopenharmony_ci } 33978c2ecf20Sopenharmony_ci 33988c2ecf20Sopenharmony_ci if (!skb_queue_empty(&dev->txq_pend)) 33998c2ecf20Sopenharmony_ci lan78xx_tx_bh(dev); 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci if (!timer_pending(&dev->delay) && 34028c2ecf20Sopenharmony_ci !test_bit(EVENT_RX_HALT, &dev->flags)) 34038c2ecf20Sopenharmony_ci lan78xx_rx_bh(dev); 34048c2ecf20Sopenharmony_ci } 34058c2ecf20Sopenharmony_ci} 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_cistatic void lan78xx_delayedwork(struct work_struct *work) 34088c2ecf20Sopenharmony_ci{ 34098c2ecf20Sopenharmony_ci int status; 34108c2ecf20Sopenharmony_ci struct lan78xx_net *dev; 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci dev = container_of(work, struct lan78xx_net, wq.work); 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_ci if (test_bit(EVENT_TX_HALT, &dev->flags)) { 34158c2ecf20Sopenharmony_ci unlink_urbs(dev, &dev->txq); 34168c2ecf20Sopenharmony_ci status = usb_autopm_get_interface(dev->intf); 34178c2ecf20Sopenharmony_ci if (status < 0) 34188c2ecf20Sopenharmony_ci goto fail_pipe; 34198c2ecf20Sopenharmony_ci status = usb_clear_halt(dev->udev, dev->pipe_out); 34208c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 34218c2ecf20Sopenharmony_ci if (status < 0 && 34228c2ecf20Sopenharmony_ci status != -EPIPE && 34238c2ecf20Sopenharmony_ci status != -ESHUTDOWN) { 34248c2ecf20Sopenharmony_ci if (netif_msg_tx_err(dev)) 34258c2ecf20Sopenharmony_cifail_pipe: 34268c2ecf20Sopenharmony_ci netdev_err(dev->net, 34278c2ecf20Sopenharmony_ci "can't clear tx halt, status %d\n", 34288c2ecf20Sopenharmony_ci status); 34298c2ecf20Sopenharmony_ci } else { 34308c2ecf20Sopenharmony_ci clear_bit(EVENT_TX_HALT, &dev->flags); 34318c2ecf20Sopenharmony_ci if (status != -ESHUTDOWN) 34328c2ecf20Sopenharmony_ci netif_wake_queue(dev->net); 34338c2ecf20Sopenharmony_ci } 34348c2ecf20Sopenharmony_ci } 34358c2ecf20Sopenharmony_ci if (test_bit(EVENT_RX_HALT, &dev->flags)) { 34368c2ecf20Sopenharmony_ci unlink_urbs(dev, &dev->rxq); 34378c2ecf20Sopenharmony_ci status = usb_autopm_get_interface(dev->intf); 34388c2ecf20Sopenharmony_ci if (status < 0) 34398c2ecf20Sopenharmony_ci goto fail_halt; 34408c2ecf20Sopenharmony_ci status = usb_clear_halt(dev->udev, dev->pipe_in); 34418c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 34428c2ecf20Sopenharmony_ci if (status < 0 && 34438c2ecf20Sopenharmony_ci status != -EPIPE && 34448c2ecf20Sopenharmony_ci status != -ESHUTDOWN) { 34458c2ecf20Sopenharmony_ci if (netif_msg_rx_err(dev)) 34468c2ecf20Sopenharmony_cifail_halt: 34478c2ecf20Sopenharmony_ci netdev_err(dev->net, 34488c2ecf20Sopenharmony_ci "can't clear rx halt, status %d\n", 34498c2ecf20Sopenharmony_ci status); 34508c2ecf20Sopenharmony_ci } else { 34518c2ecf20Sopenharmony_ci clear_bit(EVENT_RX_HALT, &dev->flags); 34528c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 34538c2ecf20Sopenharmony_ci } 34548c2ecf20Sopenharmony_ci } 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci if (test_bit(EVENT_LINK_RESET, &dev->flags)) { 34578c2ecf20Sopenharmony_ci int ret = 0; 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci clear_bit(EVENT_LINK_RESET, &dev->flags); 34608c2ecf20Sopenharmony_ci status = usb_autopm_get_interface(dev->intf); 34618c2ecf20Sopenharmony_ci if (status < 0) 34628c2ecf20Sopenharmony_ci goto skip_reset; 34638c2ecf20Sopenharmony_ci if (lan78xx_link_reset(dev) < 0) { 34648c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 34658c2ecf20Sopenharmony_ciskip_reset: 34668c2ecf20Sopenharmony_ci netdev_info(dev->net, "link reset failed (%d)\n", 34678c2ecf20Sopenharmony_ci ret); 34688c2ecf20Sopenharmony_ci } else { 34698c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->intf); 34708c2ecf20Sopenharmony_ci } 34718c2ecf20Sopenharmony_ci } 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci if (test_bit(EVENT_STAT_UPDATE, &dev->flags)) { 34748c2ecf20Sopenharmony_ci lan78xx_update_stats(dev); 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci clear_bit(EVENT_STAT_UPDATE, &dev->flags); 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci mod_timer(&dev->stat_monitor, 34798c2ecf20Sopenharmony_ci jiffies + (STAT_UPDATE_TIMER * dev->delta)); 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci dev->delta = min((dev->delta * 2), 50); 34828c2ecf20Sopenharmony_ci } 34838c2ecf20Sopenharmony_ci} 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_cistatic void intr_complete(struct urb *urb) 34868c2ecf20Sopenharmony_ci{ 34878c2ecf20Sopenharmony_ci struct lan78xx_net *dev = urb->context; 34888c2ecf20Sopenharmony_ci int status = urb->status; 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_ci switch (status) { 34918c2ecf20Sopenharmony_ci /* success */ 34928c2ecf20Sopenharmony_ci case 0: 34938c2ecf20Sopenharmony_ci lan78xx_status(dev, urb); 34948c2ecf20Sopenharmony_ci break; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci /* software-driven interface shutdown */ 34978c2ecf20Sopenharmony_ci case -ENOENT: /* urb killed */ 34988c2ecf20Sopenharmony_ci case -ESHUTDOWN: /* hardware gone */ 34998c2ecf20Sopenharmony_ci netif_dbg(dev, ifdown, dev->net, 35008c2ecf20Sopenharmony_ci "intr shutdown, code %d\n", status); 35018c2ecf20Sopenharmony_ci return; 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci /* NOTE: not throttling like RX/TX, since this endpoint 35048c2ecf20Sopenharmony_ci * already polls infrequently 35058c2ecf20Sopenharmony_ci */ 35068c2ecf20Sopenharmony_ci default: 35078c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "intr status %d\n", status); 35088c2ecf20Sopenharmony_ci break; 35098c2ecf20Sopenharmony_ci } 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci if (!netif_running(dev->net)) 35128c2ecf20Sopenharmony_ci return; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); 35158c2ecf20Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 35168c2ecf20Sopenharmony_ci if (status != 0) 35178c2ecf20Sopenharmony_ci netif_err(dev, timer, dev->net, 35188c2ecf20Sopenharmony_ci "intr resubmit --> %d\n", status); 35198c2ecf20Sopenharmony_ci} 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_cistatic void lan78xx_disconnect(struct usb_interface *intf) 35228c2ecf20Sopenharmony_ci{ 35238c2ecf20Sopenharmony_ci struct lan78xx_net *dev; 35248c2ecf20Sopenharmony_ci struct usb_device *udev; 35258c2ecf20Sopenharmony_ci struct net_device *net; 35268c2ecf20Sopenharmony_ci struct phy_device *phydev; 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_ci dev = usb_get_intfdata(intf); 35298c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 35308c2ecf20Sopenharmony_ci if (!dev) 35318c2ecf20Sopenharmony_ci return; 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci udev = interface_to_usbdev(intf); 35348c2ecf20Sopenharmony_ci net = dev->net; 35358c2ecf20Sopenharmony_ci phydev = net->phydev; 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0); 35388c2ecf20Sopenharmony_ci phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0); 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci phy_disconnect(net->phydev); 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci if (phy_is_pseudo_fixed_link(phydev)) 35438c2ecf20Sopenharmony_ci fixed_phy_unregister(phydev); 35448c2ecf20Sopenharmony_ci 35458c2ecf20Sopenharmony_ci unregister_netdev(net); 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&dev->wq); 35488c2ecf20Sopenharmony_ci 35498c2ecf20Sopenharmony_ci usb_scuttle_anchored_urbs(&dev->deferred); 35508c2ecf20Sopenharmony_ci 35518c2ecf20Sopenharmony_ci lan78xx_unbind(dev, intf); 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci usb_kill_urb(dev->urb_intr); 35548c2ecf20Sopenharmony_ci usb_free_urb(dev->urb_intr); 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_ci free_netdev(net); 35578c2ecf20Sopenharmony_ci usb_put_dev(udev); 35588c2ecf20Sopenharmony_ci} 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_cistatic void lan78xx_tx_timeout(struct net_device *net, unsigned int txqueue) 35618c2ecf20Sopenharmony_ci{ 35628c2ecf20Sopenharmony_ci struct lan78xx_net *dev = netdev_priv(net); 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci unlink_urbs(dev, &dev->txq); 35658c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 35668c2ecf20Sopenharmony_ci} 35678c2ecf20Sopenharmony_ci 35688c2ecf20Sopenharmony_cistatic netdev_features_t lan78xx_features_check(struct sk_buff *skb, 35698c2ecf20Sopenharmony_ci struct net_device *netdev, 35708c2ecf20Sopenharmony_ci netdev_features_t features) 35718c2ecf20Sopenharmony_ci{ 35728c2ecf20Sopenharmony_ci if (skb->len + TX_OVERHEAD > MAX_SINGLE_PACKET_SIZE) 35738c2ecf20Sopenharmony_ci features &= ~NETIF_F_GSO_MASK; 35748c2ecf20Sopenharmony_ci 35758c2ecf20Sopenharmony_ci features = vlan_features_check(skb, features); 35768c2ecf20Sopenharmony_ci features = vxlan_features_check(skb, features); 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci return features; 35798c2ecf20Sopenharmony_ci} 35808c2ecf20Sopenharmony_ci 35818c2ecf20Sopenharmony_cistatic const struct net_device_ops lan78xx_netdev_ops = { 35828c2ecf20Sopenharmony_ci .ndo_open = lan78xx_open, 35838c2ecf20Sopenharmony_ci .ndo_stop = lan78xx_stop, 35848c2ecf20Sopenharmony_ci .ndo_start_xmit = lan78xx_start_xmit, 35858c2ecf20Sopenharmony_ci .ndo_tx_timeout = lan78xx_tx_timeout, 35868c2ecf20Sopenharmony_ci .ndo_change_mtu = lan78xx_change_mtu, 35878c2ecf20Sopenharmony_ci .ndo_set_mac_address = lan78xx_set_mac_addr, 35888c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 35898c2ecf20Sopenharmony_ci .ndo_do_ioctl = phy_do_ioctl_running, 35908c2ecf20Sopenharmony_ci .ndo_set_rx_mode = lan78xx_set_multicast, 35918c2ecf20Sopenharmony_ci .ndo_set_features = lan78xx_set_features, 35928c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = lan78xx_vlan_rx_add_vid, 35938c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = lan78xx_vlan_rx_kill_vid, 35948c2ecf20Sopenharmony_ci .ndo_features_check = lan78xx_features_check, 35958c2ecf20Sopenharmony_ci}; 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_cistatic void lan78xx_stat_monitor(struct timer_list *t) 35988c2ecf20Sopenharmony_ci{ 35998c2ecf20Sopenharmony_ci struct lan78xx_net *dev = from_timer(dev, t, stat_monitor); 36008c2ecf20Sopenharmony_ci 36018c2ecf20Sopenharmony_ci lan78xx_defer_kevent(dev, EVENT_STAT_UPDATE); 36028c2ecf20Sopenharmony_ci} 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_cistatic int lan78xx_probe(struct usb_interface *intf, 36058c2ecf20Sopenharmony_ci const struct usb_device_id *id) 36068c2ecf20Sopenharmony_ci{ 36078c2ecf20Sopenharmony_ci struct usb_host_endpoint *ep_blkin, *ep_blkout, *ep_intr; 36088c2ecf20Sopenharmony_ci struct lan78xx_net *dev; 36098c2ecf20Sopenharmony_ci struct net_device *netdev; 36108c2ecf20Sopenharmony_ci struct usb_device *udev; 36118c2ecf20Sopenharmony_ci int ret; 36128c2ecf20Sopenharmony_ci unsigned maxp; 36138c2ecf20Sopenharmony_ci unsigned period; 36148c2ecf20Sopenharmony_ci u8 *buf = NULL; 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci udev = interface_to_usbdev(intf); 36178c2ecf20Sopenharmony_ci udev = usb_get_dev(udev); 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct lan78xx_net)); 36208c2ecf20Sopenharmony_ci if (!netdev) { 36218c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Error: OOM\n"); 36228c2ecf20Sopenharmony_ci ret = -ENOMEM; 36238c2ecf20Sopenharmony_ci goto out1; 36248c2ecf20Sopenharmony_ci } 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci /* netdev_printk() needs this */ 36278c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &intf->dev); 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci dev = netdev_priv(netdev); 36308c2ecf20Sopenharmony_ci dev->udev = udev; 36318c2ecf20Sopenharmony_ci dev->intf = intf; 36328c2ecf20Sopenharmony_ci dev->net = netdev; 36338c2ecf20Sopenharmony_ci dev->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV 36348c2ecf20Sopenharmony_ci | NETIF_MSG_PROBE | NETIF_MSG_LINK); 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci skb_queue_head_init(&dev->rxq); 36378c2ecf20Sopenharmony_ci skb_queue_head_init(&dev->txq); 36388c2ecf20Sopenharmony_ci skb_queue_head_init(&dev->done); 36398c2ecf20Sopenharmony_ci skb_queue_head_init(&dev->rxq_pause); 36408c2ecf20Sopenharmony_ci skb_queue_head_init(&dev->txq_pend); 36418c2ecf20Sopenharmony_ci mutex_init(&dev->phy_mutex); 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ci tasklet_init(&dev->bh, lan78xx_bh, (unsigned long)dev); 36448c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&dev->wq, lan78xx_delayedwork); 36458c2ecf20Sopenharmony_ci init_usb_anchor(&dev->deferred); 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci netdev->netdev_ops = &lan78xx_netdev_ops; 36488c2ecf20Sopenharmony_ci netdev->watchdog_timeo = TX_TIMEOUT_JIFFIES; 36498c2ecf20Sopenharmony_ci netdev->ethtool_ops = &lan78xx_ethtool_ops; 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_ci dev->delta = 1; 36528c2ecf20Sopenharmony_ci timer_setup(&dev->stat_monitor, lan78xx_stat_monitor, 0); 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_ci mutex_init(&dev->stats.access_lock); 36558c2ecf20Sopenharmony_ci 36568c2ecf20Sopenharmony_ci if (intf->cur_altsetting->desc.bNumEndpoints < 3) { 36578c2ecf20Sopenharmony_ci ret = -ENODEV; 36588c2ecf20Sopenharmony_ci goto out2; 36598c2ecf20Sopenharmony_ci } 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci dev->pipe_in = usb_rcvbulkpipe(udev, BULK_IN_PIPE); 36628c2ecf20Sopenharmony_ci ep_blkin = usb_pipe_endpoint(udev, dev->pipe_in); 36638c2ecf20Sopenharmony_ci if (!ep_blkin || !usb_endpoint_is_bulk_in(&ep_blkin->desc)) { 36648c2ecf20Sopenharmony_ci ret = -ENODEV; 36658c2ecf20Sopenharmony_ci goto out2; 36668c2ecf20Sopenharmony_ci } 36678c2ecf20Sopenharmony_ci 36688c2ecf20Sopenharmony_ci dev->pipe_out = usb_sndbulkpipe(udev, BULK_OUT_PIPE); 36698c2ecf20Sopenharmony_ci ep_blkout = usb_pipe_endpoint(udev, dev->pipe_out); 36708c2ecf20Sopenharmony_ci if (!ep_blkout || !usb_endpoint_is_bulk_out(&ep_blkout->desc)) { 36718c2ecf20Sopenharmony_ci ret = -ENODEV; 36728c2ecf20Sopenharmony_ci goto out2; 36738c2ecf20Sopenharmony_ci } 36748c2ecf20Sopenharmony_ci 36758c2ecf20Sopenharmony_ci ep_intr = &intf->cur_altsetting->endpoint[2]; 36768c2ecf20Sopenharmony_ci if (!usb_endpoint_is_int_in(&ep_intr->desc)) { 36778c2ecf20Sopenharmony_ci ret = -ENODEV; 36788c2ecf20Sopenharmony_ci goto out2; 36798c2ecf20Sopenharmony_ci } 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci dev->pipe_intr = usb_rcvintpipe(dev->udev, 36828c2ecf20Sopenharmony_ci usb_endpoint_num(&ep_intr->desc)); 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci ret = lan78xx_bind(dev, intf); 36858c2ecf20Sopenharmony_ci if (ret < 0) 36868c2ecf20Sopenharmony_ci goto out2; 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci if (netdev->mtu > (dev->hard_mtu - netdev->hard_header_len)) 36898c2ecf20Sopenharmony_ci netdev->mtu = dev->hard_mtu - netdev->hard_header_len; 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci /* MTU range: 68 - 9000 */ 36928c2ecf20Sopenharmony_ci netdev->max_mtu = MAX_SINGLE_PACKET_SIZE; 36938c2ecf20Sopenharmony_ci netif_set_gso_max_size(netdev, MAX_SINGLE_PACKET_SIZE - MAX_HEADER); 36948c2ecf20Sopenharmony_ci 36958c2ecf20Sopenharmony_ci period = ep_intr->desc.bInterval; 36968c2ecf20Sopenharmony_ci maxp = usb_maxpacket(dev->udev, dev->pipe_intr, 0); 36978c2ecf20Sopenharmony_ci buf = kmalloc(maxp, GFP_KERNEL); 36988c2ecf20Sopenharmony_ci if (buf) { 36998c2ecf20Sopenharmony_ci dev->urb_intr = usb_alloc_urb(0, GFP_KERNEL); 37008c2ecf20Sopenharmony_ci if (!dev->urb_intr) { 37018c2ecf20Sopenharmony_ci ret = -ENOMEM; 37028c2ecf20Sopenharmony_ci kfree(buf); 37038c2ecf20Sopenharmony_ci goto out3; 37048c2ecf20Sopenharmony_ci } else { 37058c2ecf20Sopenharmony_ci usb_fill_int_urb(dev->urb_intr, dev->udev, 37068c2ecf20Sopenharmony_ci dev->pipe_intr, buf, maxp, 37078c2ecf20Sopenharmony_ci intr_complete, dev, period); 37088c2ecf20Sopenharmony_ci dev->urb_intr->transfer_flags |= URB_FREE_BUFFER; 37098c2ecf20Sopenharmony_ci } 37108c2ecf20Sopenharmony_ci } 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out, 1); 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci /* Reject broken descriptors. */ 37158c2ecf20Sopenharmony_ci if (dev->maxpacket == 0) { 37168c2ecf20Sopenharmony_ci ret = -ENODEV; 37178c2ecf20Sopenharmony_ci goto out4; 37188c2ecf20Sopenharmony_ci } 37198c2ecf20Sopenharmony_ci 37208c2ecf20Sopenharmony_ci /* driver requires remote-wakeup capability during autosuspend. */ 37218c2ecf20Sopenharmony_ci intf->needs_remote_wakeup = 1; 37228c2ecf20Sopenharmony_ci 37238c2ecf20Sopenharmony_ci ret = lan78xx_phy_init(dev); 37248c2ecf20Sopenharmony_ci if (ret < 0) 37258c2ecf20Sopenharmony_ci goto out4; 37268c2ecf20Sopenharmony_ci 37278c2ecf20Sopenharmony_ci ret = register_netdev(netdev); 37288c2ecf20Sopenharmony_ci if (ret != 0) { 37298c2ecf20Sopenharmony_ci netif_err(dev, probe, netdev, "couldn't register the device\n"); 37308c2ecf20Sopenharmony_ci goto out5; 37318c2ecf20Sopenharmony_ci } 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci usb_set_intfdata(intf, dev); 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci ret = device_set_wakeup_enable(&udev->dev, true); 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci /* Default delay of 2sec has more overhead than advantage. 37388c2ecf20Sopenharmony_ci * Set to 10sec as default. 37398c2ecf20Sopenharmony_ci */ 37408c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&udev->dev, 37418c2ecf20Sopenharmony_ci DEFAULT_AUTOSUSPEND_DELAY); 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci return 0; 37448c2ecf20Sopenharmony_ci 37458c2ecf20Sopenharmony_ciout5: 37468c2ecf20Sopenharmony_ci phy_disconnect(netdev->phydev); 37478c2ecf20Sopenharmony_ciout4: 37488c2ecf20Sopenharmony_ci usb_free_urb(dev->urb_intr); 37498c2ecf20Sopenharmony_ciout3: 37508c2ecf20Sopenharmony_ci lan78xx_unbind(dev, intf); 37518c2ecf20Sopenharmony_ciout2: 37528c2ecf20Sopenharmony_ci free_netdev(netdev); 37538c2ecf20Sopenharmony_ciout1: 37548c2ecf20Sopenharmony_ci usb_put_dev(udev); 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci return ret; 37578c2ecf20Sopenharmony_ci} 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_cistatic u16 lan78xx_wakeframe_crc16(const u8 *buf, int len) 37608c2ecf20Sopenharmony_ci{ 37618c2ecf20Sopenharmony_ci const u16 crc16poly = 0x8005; 37628c2ecf20Sopenharmony_ci int i; 37638c2ecf20Sopenharmony_ci u16 bit, crc, msb; 37648c2ecf20Sopenharmony_ci u8 data; 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci crc = 0xFFFF; 37678c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 37688c2ecf20Sopenharmony_ci data = *buf++; 37698c2ecf20Sopenharmony_ci for (bit = 0; bit < 8; bit++) { 37708c2ecf20Sopenharmony_ci msb = crc >> 15; 37718c2ecf20Sopenharmony_ci crc <<= 1; 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci if (msb ^ (u16)(data & 1)) { 37748c2ecf20Sopenharmony_ci crc ^= crc16poly; 37758c2ecf20Sopenharmony_ci crc |= (u16)0x0001U; 37768c2ecf20Sopenharmony_ci } 37778c2ecf20Sopenharmony_ci data >>= 1; 37788c2ecf20Sopenharmony_ci } 37798c2ecf20Sopenharmony_ci } 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_ci return crc; 37828c2ecf20Sopenharmony_ci} 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_cistatic int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol) 37858c2ecf20Sopenharmony_ci{ 37868c2ecf20Sopenharmony_ci u32 buf; 37878c2ecf20Sopenharmony_ci int mask_index; 37888c2ecf20Sopenharmony_ci u16 crc; 37898c2ecf20Sopenharmony_ci u32 temp_wucsr; 37908c2ecf20Sopenharmony_ci u32 temp_pmt_ctl; 37918c2ecf20Sopenharmony_ci const u8 ipv4_multicast[3] = { 0x01, 0x00, 0x5E }; 37928c2ecf20Sopenharmony_ci const u8 ipv6_multicast[3] = { 0x33, 0x33 }; 37938c2ecf20Sopenharmony_ci const u8 arp_type[2] = { 0x08, 0x06 }; 37948c2ecf20Sopenharmony_ci 37958c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, MAC_TX, &buf); 37968c2ecf20Sopenharmony_ci buf &= ~MAC_TX_TXEN_; 37978c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_TX, buf); 37988c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, MAC_RX, &buf); 37998c2ecf20Sopenharmony_ci buf &= ~MAC_RX_RXEN_; 38008c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_RX, buf); 38018c2ecf20Sopenharmony_ci 38028c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUCSR, 0); 38038c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUCSR2, 0); 38048c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci temp_wucsr = 0; 38078c2ecf20Sopenharmony_ci 38088c2ecf20Sopenharmony_ci temp_pmt_ctl = 0; 38098c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, PMT_CTL, &temp_pmt_ctl); 38108c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_RES_CLR_WKP_EN_; 38118c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_RES_CLR_WKP_STS_; 38128c2ecf20Sopenharmony_ci 38138c2ecf20Sopenharmony_ci for (mask_index = 0; mask_index < NUM_OF_WUF_CFG; mask_index++) 38148c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_CFG(mask_index), 0); 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci mask_index = 0; 38178c2ecf20Sopenharmony_ci if (wol & WAKE_PHY) { 38188c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_PHY_WAKE_EN_; 38198c2ecf20Sopenharmony_ci 38208c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 38218c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 38228c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; 38238c2ecf20Sopenharmony_ci } 38248c2ecf20Sopenharmony_ci if (wol & WAKE_MAGIC) { 38258c2ecf20Sopenharmony_ci temp_wucsr |= WUCSR_MPEN_; 38268c2ecf20Sopenharmony_ci 38278c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 38288c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 38298c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_3_; 38308c2ecf20Sopenharmony_ci } 38318c2ecf20Sopenharmony_ci if (wol & WAKE_BCAST) { 38328c2ecf20Sopenharmony_ci temp_wucsr |= WUCSR_BCST_EN_; 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 38358c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 38368c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; 38378c2ecf20Sopenharmony_ci } 38388c2ecf20Sopenharmony_ci if (wol & WAKE_MCAST) { 38398c2ecf20Sopenharmony_ci temp_wucsr |= WUCSR_WAKE_EN_; 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci /* set WUF_CFG & WUF_MASK for IPv4 Multicast */ 38428c2ecf20Sopenharmony_ci crc = lan78xx_wakeframe_crc16(ipv4_multicast, 3); 38438c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_CFG(mask_index), 38448c2ecf20Sopenharmony_ci WUF_CFGX_EN_ | 38458c2ecf20Sopenharmony_ci WUF_CFGX_TYPE_MCAST_ | 38468c2ecf20Sopenharmony_ci (0 << WUF_CFGX_OFFSET_SHIFT_) | 38478c2ecf20Sopenharmony_ci (crc & WUF_CFGX_CRC16_MASK_)); 38488c2ecf20Sopenharmony_ci 38498c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK0(mask_index), 7); 38508c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); 38518c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); 38528c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); 38538c2ecf20Sopenharmony_ci mask_index++; 38548c2ecf20Sopenharmony_ci 38558c2ecf20Sopenharmony_ci /* for IPv6 Multicast */ 38568c2ecf20Sopenharmony_ci crc = lan78xx_wakeframe_crc16(ipv6_multicast, 2); 38578c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_CFG(mask_index), 38588c2ecf20Sopenharmony_ci WUF_CFGX_EN_ | 38598c2ecf20Sopenharmony_ci WUF_CFGX_TYPE_MCAST_ | 38608c2ecf20Sopenharmony_ci (0 << WUF_CFGX_OFFSET_SHIFT_) | 38618c2ecf20Sopenharmony_ci (crc & WUF_CFGX_CRC16_MASK_)); 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK0(mask_index), 3); 38648c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); 38658c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); 38668c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); 38678c2ecf20Sopenharmony_ci mask_index++; 38688c2ecf20Sopenharmony_ci 38698c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 38708c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 38718c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; 38728c2ecf20Sopenharmony_ci } 38738c2ecf20Sopenharmony_ci if (wol & WAKE_UCAST) { 38748c2ecf20Sopenharmony_ci temp_wucsr |= WUCSR_PFDA_EN_; 38758c2ecf20Sopenharmony_ci 38768c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 38778c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 38788c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; 38798c2ecf20Sopenharmony_ci } 38808c2ecf20Sopenharmony_ci if (wol & WAKE_ARP) { 38818c2ecf20Sopenharmony_ci temp_wucsr |= WUCSR_WAKE_EN_; 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci /* set WUF_CFG & WUF_MASK 38848c2ecf20Sopenharmony_ci * for packettype (offset 12,13) = ARP (0x0806) 38858c2ecf20Sopenharmony_ci */ 38868c2ecf20Sopenharmony_ci crc = lan78xx_wakeframe_crc16(arp_type, 2); 38878c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_CFG(mask_index), 38888c2ecf20Sopenharmony_ci WUF_CFGX_EN_ | 38898c2ecf20Sopenharmony_ci WUF_CFGX_TYPE_ALL_ | 38908c2ecf20Sopenharmony_ci (0 << WUF_CFGX_OFFSET_SHIFT_) | 38918c2ecf20Sopenharmony_ci (crc & WUF_CFGX_CRC16_MASK_)); 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK0(mask_index), 0x3000); 38948c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0); 38958c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0); 38968c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0); 38978c2ecf20Sopenharmony_ci mask_index++; 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 39008c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 39018c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; 39028c2ecf20Sopenharmony_ci } 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, WUCSR, temp_wucsr); 39058c2ecf20Sopenharmony_ci 39068c2ecf20Sopenharmony_ci /* when multiple WOL bits are set */ 39078c2ecf20Sopenharmony_ci if (hweight_long((unsigned long)wol) > 1) { 39088c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_WOL_EN_; 39098c2ecf20Sopenharmony_ci temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_; 39108c2ecf20Sopenharmony_ci temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_; 39118c2ecf20Sopenharmony_ci } 39128c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, PMT_CTL, temp_pmt_ctl); 39138c2ecf20Sopenharmony_ci 39148c2ecf20Sopenharmony_ci /* clear WUPS */ 39158c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, PMT_CTL, &buf); 39168c2ecf20Sopenharmony_ci buf |= PMT_CTL_WUPS_MASK_; 39178c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, PMT_CTL, buf); 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ci lan78xx_read_reg(dev, MAC_RX, &buf); 39208c2ecf20Sopenharmony_ci buf |= MAC_RX_RXEN_; 39218c2ecf20Sopenharmony_ci lan78xx_write_reg(dev, MAC_RX, buf); 39228c2ecf20Sopenharmony_ci 39238c2ecf20Sopenharmony_ci return 0; 39248c2ecf20Sopenharmony_ci} 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_cistatic int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) 39278c2ecf20Sopenharmony_ci{ 39288c2ecf20Sopenharmony_ci struct lan78xx_net *dev = usb_get_intfdata(intf); 39298c2ecf20Sopenharmony_ci struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); 39308c2ecf20Sopenharmony_ci u32 buf; 39318c2ecf20Sopenharmony_ci int ret; 39328c2ecf20Sopenharmony_ci 39338c2ecf20Sopenharmony_ci if (!dev->suspend_count++) { 39348c2ecf20Sopenharmony_ci spin_lock_irq(&dev->txq.lock); 39358c2ecf20Sopenharmony_ci /* don't autosuspend while transmitting */ 39368c2ecf20Sopenharmony_ci if ((skb_queue_len(&dev->txq) || 39378c2ecf20Sopenharmony_ci skb_queue_len(&dev->txq_pend)) && 39388c2ecf20Sopenharmony_ci PMSG_IS_AUTO(message)) { 39398c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->txq.lock); 39408c2ecf20Sopenharmony_ci ret = -EBUSY; 39418c2ecf20Sopenharmony_ci goto out; 39428c2ecf20Sopenharmony_ci } else { 39438c2ecf20Sopenharmony_ci set_bit(EVENT_DEV_ASLEEP, &dev->flags); 39448c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->txq.lock); 39458c2ecf20Sopenharmony_ci } 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci /* stop TX & RX */ 39488c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_TX, &buf); 39498c2ecf20Sopenharmony_ci buf &= ~MAC_TX_TXEN_; 39508c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_TX, buf); 39518c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_RX, &buf); 39528c2ecf20Sopenharmony_ci buf &= ~MAC_RX_RXEN_; 39538c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_RX, buf); 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_ci /* empty out the rx and queues */ 39568c2ecf20Sopenharmony_ci netif_device_detach(dev->net); 39578c2ecf20Sopenharmony_ci lan78xx_terminate_urbs(dev); 39588c2ecf20Sopenharmony_ci usb_kill_urb(dev->urb_intr); 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci /* reattach */ 39618c2ecf20Sopenharmony_ci netif_device_attach(dev->net); 39628c2ecf20Sopenharmony_ci } 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { 39658c2ecf20Sopenharmony_ci del_timer(&dev->stat_monitor); 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci if (PMSG_IS_AUTO(message)) { 39688c2ecf20Sopenharmony_ci /* auto suspend (selective suspend) */ 39698c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_TX, &buf); 39708c2ecf20Sopenharmony_ci buf &= ~MAC_TX_TXEN_; 39718c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_TX, buf); 39728c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_RX, &buf); 39738c2ecf20Sopenharmony_ci buf &= ~MAC_RX_RXEN_; 39748c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_RX, buf); 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR, 0); 39778c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR2, 0); 39788c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); 39798c2ecf20Sopenharmony_ci 39808c2ecf20Sopenharmony_ci /* set goodframe wakeup */ 39818c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, WUCSR, &buf); 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_ci buf |= WUCSR_RFE_WAKE_EN_; 39848c2ecf20Sopenharmony_ci buf |= WUCSR_STORE_WAKE_; 39858c2ecf20Sopenharmony_ci 39868c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR, buf); 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, PMT_CTL, &buf); 39898c2ecf20Sopenharmony_ci 39908c2ecf20Sopenharmony_ci buf &= ~PMT_CTL_RES_CLR_WKP_EN_; 39918c2ecf20Sopenharmony_ci buf |= PMT_CTL_RES_CLR_WKP_STS_; 39928c2ecf20Sopenharmony_ci 39938c2ecf20Sopenharmony_ci buf |= PMT_CTL_PHY_WAKE_EN_; 39948c2ecf20Sopenharmony_ci buf |= PMT_CTL_WOL_EN_; 39958c2ecf20Sopenharmony_ci buf &= ~PMT_CTL_SUS_MODE_MASK_; 39968c2ecf20Sopenharmony_ci buf |= PMT_CTL_SUS_MODE_3_; 39978c2ecf20Sopenharmony_ci 39988c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, PMT_CTL, buf); 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, PMT_CTL, &buf); 40018c2ecf20Sopenharmony_ci 40028c2ecf20Sopenharmony_ci buf |= PMT_CTL_WUPS_MASK_; 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, PMT_CTL, buf); 40058c2ecf20Sopenharmony_ci 40068c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_RX, &buf); 40078c2ecf20Sopenharmony_ci buf |= MAC_RX_RXEN_; 40088c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_RX, buf); 40098c2ecf20Sopenharmony_ci } else { 40108c2ecf20Sopenharmony_ci lan78xx_set_suspend(dev, pdata->wol); 40118c2ecf20Sopenharmony_ci } 40128c2ecf20Sopenharmony_ci } 40138c2ecf20Sopenharmony_ci 40148c2ecf20Sopenharmony_ci ret = 0; 40158c2ecf20Sopenharmony_ciout: 40168c2ecf20Sopenharmony_ci return ret; 40178c2ecf20Sopenharmony_ci} 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_cistatic int lan78xx_resume(struct usb_interface *intf) 40208c2ecf20Sopenharmony_ci{ 40218c2ecf20Sopenharmony_ci struct lan78xx_net *dev = usb_get_intfdata(intf); 40228c2ecf20Sopenharmony_ci struct sk_buff *skb; 40238c2ecf20Sopenharmony_ci struct urb *res; 40248c2ecf20Sopenharmony_ci int ret; 40258c2ecf20Sopenharmony_ci u32 buf; 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci if (!timer_pending(&dev->stat_monitor)) { 40288c2ecf20Sopenharmony_ci dev->delta = 1; 40298c2ecf20Sopenharmony_ci mod_timer(&dev->stat_monitor, 40308c2ecf20Sopenharmony_ci jiffies + STAT_UPDATE_TIMER); 40318c2ecf20Sopenharmony_ci } 40328c2ecf20Sopenharmony_ci 40338c2ecf20Sopenharmony_ci if (!--dev->suspend_count) { 40348c2ecf20Sopenharmony_ci /* resume interrupt URBs */ 40358c2ecf20Sopenharmony_ci if (dev->urb_intr && test_bit(EVENT_DEV_OPEN, &dev->flags)) 40368c2ecf20Sopenharmony_ci usb_submit_urb(dev->urb_intr, GFP_NOIO); 40378c2ecf20Sopenharmony_ci 40388c2ecf20Sopenharmony_ci spin_lock_irq(&dev->txq.lock); 40398c2ecf20Sopenharmony_ci while ((res = usb_get_from_anchor(&dev->deferred))) { 40408c2ecf20Sopenharmony_ci skb = (struct sk_buff *)res->context; 40418c2ecf20Sopenharmony_ci ret = usb_submit_urb(res, GFP_ATOMIC); 40428c2ecf20Sopenharmony_ci if (ret < 0) { 40438c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 40448c2ecf20Sopenharmony_ci usb_free_urb(res); 40458c2ecf20Sopenharmony_ci usb_autopm_put_interface_async(dev->intf); 40468c2ecf20Sopenharmony_ci } else { 40478c2ecf20Sopenharmony_ci netif_trans_update(dev->net); 40488c2ecf20Sopenharmony_ci lan78xx_queue_skb(&dev->txq, skb, tx_start); 40498c2ecf20Sopenharmony_ci } 40508c2ecf20Sopenharmony_ci } 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci clear_bit(EVENT_DEV_ASLEEP, &dev->flags); 40538c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->txq.lock); 40548c2ecf20Sopenharmony_ci 40558c2ecf20Sopenharmony_ci if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { 40568c2ecf20Sopenharmony_ci if (!(skb_queue_len(&dev->txq) >= dev->tx_qlen)) 40578c2ecf20Sopenharmony_ci netif_start_queue(dev->net); 40588c2ecf20Sopenharmony_ci tasklet_schedule(&dev->bh); 40598c2ecf20Sopenharmony_ci } 40608c2ecf20Sopenharmony_ci } 40618c2ecf20Sopenharmony_ci 40628c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR2, 0); 40638c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR, 0); 40648c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL); 40658c2ecf20Sopenharmony_ci 40668c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR2, WUCSR2_NS_RCD_ | 40678c2ecf20Sopenharmony_ci WUCSR2_ARP_RCD_ | 40688c2ecf20Sopenharmony_ci WUCSR2_IPV6_TCPSYN_RCD_ | 40698c2ecf20Sopenharmony_ci WUCSR2_IPV4_TCPSYN_RCD_); 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, WUCSR, WUCSR_EEE_TX_WAKE_ | 40728c2ecf20Sopenharmony_ci WUCSR_EEE_RX_WAKE_ | 40738c2ecf20Sopenharmony_ci WUCSR_PFDA_FR_ | 40748c2ecf20Sopenharmony_ci WUCSR_RFE_WAKE_FR_ | 40758c2ecf20Sopenharmony_ci WUCSR_WUFR_ | 40768c2ecf20Sopenharmony_ci WUCSR_MPR_ | 40778c2ecf20Sopenharmony_ci WUCSR_BCST_FR_); 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_ci ret = lan78xx_read_reg(dev, MAC_TX, &buf); 40808c2ecf20Sopenharmony_ci buf |= MAC_TX_TXEN_; 40818c2ecf20Sopenharmony_ci ret = lan78xx_write_reg(dev, MAC_TX, buf); 40828c2ecf20Sopenharmony_ci 40838c2ecf20Sopenharmony_ci return 0; 40848c2ecf20Sopenharmony_ci} 40858c2ecf20Sopenharmony_ci 40868c2ecf20Sopenharmony_cistatic int lan78xx_reset_resume(struct usb_interface *intf) 40878c2ecf20Sopenharmony_ci{ 40888c2ecf20Sopenharmony_ci struct lan78xx_net *dev = usb_get_intfdata(intf); 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci lan78xx_reset(dev); 40918c2ecf20Sopenharmony_ci 40928c2ecf20Sopenharmony_ci phy_start(dev->net->phydev); 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_ci return lan78xx_resume(intf); 40958c2ecf20Sopenharmony_ci} 40968c2ecf20Sopenharmony_ci 40978c2ecf20Sopenharmony_cistatic const struct usb_device_id products[] = { 40988c2ecf20Sopenharmony_ci { 40998c2ecf20Sopenharmony_ci /* LAN7800 USB Gigabit Ethernet Device */ 41008c2ecf20Sopenharmony_ci USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7800_USB_PRODUCT_ID), 41018c2ecf20Sopenharmony_ci }, 41028c2ecf20Sopenharmony_ci { 41038c2ecf20Sopenharmony_ci /* LAN7850 USB Gigabit Ethernet Device */ 41048c2ecf20Sopenharmony_ci USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7850_USB_PRODUCT_ID), 41058c2ecf20Sopenharmony_ci }, 41068c2ecf20Sopenharmony_ci { 41078c2ecf20Sopenharmony_ci /* LAN7801 USB Gigabit Ethernet Device */ 41088c2ecf20Sopenharmony_ci USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7801_USB_PRODUCT_ID), 41098c2ecf20Sopenharmony_ci }, 41108c2ecf20Sopenharmony_ci { 41118c2ecf20Sopenharmony_ci /* ATM2-AF USB Gigabit Ethernet Device */ 41128c2ecf20Sopenharmony_ci USB_DEVICE(AT29M2AF_USB_VENDOR_ID, AT29M2AF_USB_PRODUCT_ID), 41138c2ecf20Sopenharmony_ci }, 41148c2ecf20Sopenharmony_ci {}, 41158c2ecf20Sopenharmony_ci}; 41168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 41178c2ecf20Sopenharmony_ci 41188c2ecf20Sopenharmony_cistatic struct usb_driver lan78xx_driver = { 41198c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 41208c2ecf20Sopenharmony_ci .id_table = products, 41218c2ecf20Sopenharmony_ci .probe = lan78xx_probe, 41228c2ecf20Sopenharmony_ci .disconnect = lan78xx_disconnect, 41238c2ecf20Sopenharmony_ci .suspend = lan78xx_suspend, 41248c2ecf20Sopenharmony_ci .resume = lan78xx_resume, 41258c2ecf20Sopenharmony_ci .reset_resume = lan78xx_reset_resume, 41268c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 41278c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 41288c2ecf20Sopenharmony_ci}; 41298c2ecf20Sopenharmony_ci 41308c2ecf20Sopenharmony_cimodule_usb_driver(lan78xx_driver); 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 41338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 41348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4135