162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015 Microchip Technology
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/netdevice.h>
762306a36Sopenharmony_ci#include <linux/etherdevice.h>
862306a36Sopenharmony_ci#include <linux/ethtool.h>
962306a36Sopenharmony_ci#include <linux/usb.h>
1062306a36Sopenharmony_ci#include <linux/crc32.h>
1162306a36Sopenharmony_ci#include <linux/signal.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/if_vlan.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include <linux/linkmode.h>
1662306a36Sopenharmony_ci#include <linux/list.h>
1762306a36Sopenharmony_ci#include <linux/ip.h>
1862306a36Sopenharmony_ci#include <linux/ipv6.h>
1962306a36Sopenharmony_ci#include <linux/mdio.h>
2062306a36Sopenharmony_ci#include <linux/phy.h>
2162306a36Sopenharmony_ci#include <net/ip6_checksum.h>
2262306a36Sopenharmony_ci#include <net/vxlan.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/irqdomain.h>
2562306a36Sopenharmony_ci#include <linux/irq.h>
2662306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
2762306a36Sopenharmony_ci#include <linux/microchipphy.h>
2862306a36Sopenharmony_ci#include <linux/phy_fixed.h>
2962306a36Sopenharmony_ci#include <linux/of_mdio.h>
3062306a36Sopenharmony_ci#include <linux/of_net.h>
3162306a36Sopenharmony_ci#include "lan78xx.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define DRIVER_AUTHOR	"WOOJUNG HUH <woojung.huh@microchip.com>"
3462306a36Sopenharmony_ci#define DRIVER_DESC	"LAN78XX USB 3.0 Gigabit Ethernet Devices"
3562306a36Sopenharmony_ci#define DRIVER_NAME	"lan78xx"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define TX_TIMEOUT_JIFFIES		(5 * HZ)
3862306a36Sopenharmony_ci#define THROTTLE_JIFFIES		(HZ / 8)
3962306a36Sopenharmony_ci#define UNLINK_TIMEOUT_MS		3
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define RX_MAX_QUEUE_MEMORY		(60 * 1518)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define SS_USB_PKT_SIZE			(1024)
4462306a36Sopenharmony_ci#define HS_USB_PKT_SIZE			(512)
4562306a36Sopenharmony_ci#define FS_USB_PKT_SIZE			(64)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MAX_RX_FIFO_SIZE		(12 * 1024)
4862306a36Sopenharmony_ci#define MAX_TX_FIFO_SIZE		(12 * 1024)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define FLOW_THRESHOLD(n)		((((n) + 511) / 512) & 0x7F)
5162306a36Sopenharmony_ci#define FLOW_CTRL_THRESHOLD(on, off)	((FLOW_THRESHOLD(on)  << 0) | \
5262306a36Sopenharmony_ci					 (FLOW_THRESHOLD(off) << 8))
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Flow control turned on when Rx FIFO level rises above this level (bytes) */
5562306a36Sopenharmony_ci#define FLOW_ON_SS			9216
5662306a36Sopenharmony_ci#define FLOW_ON_HS			8704
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Flow control turned off when Rx FIFO level falls below this level (bytes) */
5962306a36Sopenharmony_ci#define FLOW_OFF_SS			4096
6062306a36Sopenharmony_ci#define FLOW_OFF_HS			1024
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define DEFAULT_BURST_CAP_SIZE		(MAX_TX_FIFO_SIZE)
6362306a36Sopenharmony_ci#define DEFAULT_BULK_IN_DELAY		(0x0800)
6462306a36Sopenharmony_ci#define MAX_SINGLE_PACKET_SIZE		(9000)
6562306a36Sopenharmony_ci#define DEFAULT_TX_CSUM_ENABLE		(true)
6662306a36Sopenharmony_ci#define DEFAULT_RX_CSUM_ENABLE		(true)
6762306a36Sopenharmony_ci#define DEFAULT_TSO_CSUM_ENABLE		(true)
6862306a36Sopenharmony_ci#define DEFAULT_VLAN_FILTER_ENABLE	(true)
6962306a36Sopenharmony_ci#define DEFAULT_VLAN_RX_OFFLOAD		(true)
7062306a36Sopenharmony_ci#define TX_ALIGNMENT			(4)
7162306a36Sopenharmony_ci#define RXW_PADDING			2
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define LAN78XX_USB_VENDOR_ID		(0x0424)
7462306a36Sopenharmony_ci#define LAN7800_USB_PRODUCT_ID		(0x7800)
7562306a36Sopenharmony_ci#define LAN7850_USB_PRODUCT_ID		(0x7850)
7662306a36Sopenharmony_ci#define LAN7801_USB_PRODUCT_ID		(0x7801)
7762306a36Sopenharmony_ci#define LAN78XX_EEPROM_MAGIC		(0x78A5)
7862306a36Sopenharmony_ci#define LAN78XX_OTP_MAGIC		(0x78F3)
7962306a36Sopenharmony_ci#define AT29M2AF_USB_VENDOR_ID		(0x07C9)
8062306a36Sopenharmony_ci#define AT29M2AF_USB_PRODUCT_ID	(0x0012)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define	MII_READ			1
8362306a36Sopenharmony_ci#define	MII_WRITE			0
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define EEPROM_INDICATOR		(0xA5)
8662306a36Sopenharmony_ci#define EEPROM_MAC_OFFSET		(0x01)
8762306a36Sopenharmony_ci#define MAX_EEPROM_SIZE			512
8862306a36Sopenharmony_ci#define OTP_INDICATOR_1			(0xF3)
8962306a36Sopenharmony_ci#define OTP_INDICATOR_2			(0xF7)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define WAKE_ALL			(WAKE_PHY | WAKE_UCAST | \
9262306a36Sopenharmony_ci					 WAKE_MCAST | WAKE_BCAST | \
9362306a36Sopenharmony_ci					 WAKE_ARP | WAKE_MAGIC)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define TX_URB_NUM			10
9662306a36Sopenharmony_ci#define TX_SS_URB_NUM			TX_URB_NUM
9762306a36Sopenharmony_ci#define TX_HS_URB_NUM			TX_URB_NUM
9862306a36Sopenharmony_ci#define TX_FS_URB_NUM			TX_URB_NUM
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* A single URB buffer must be large enough to hold a complete jumbo packet
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci#define TX_SS_URB_SIZE			(32 * 1024)
10362306a36Sopenharmony_ci#define TX_HS_URB_SIZE			(16 * 1024)
10462306a36Sopenharmony_ci#define TX_FS_URB_SIZE			(10 * 1024)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define RX_SS_URB_NUM			30
10762306a36Sopenharmony_ci#define RX_HS_URB_NUM			10
10862306a36Sopenharmony_ci#define RX_FS_URB_NUM			10
10962306a36Sopenharmony_ci#define RX_SS_URB_SIZE			TX_SS_URB_SIZE
11062306a36Sopenharmony_ci#define RX_HS_URB_SIZE			TX_HS_URB_SIZE
11162306a36Sopenharmony_ci#define RX_FS_URB_SIZE			TX_FS_URB_SIZE
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define SS_BURST_CAP_SIZE		RX_SS_URB_SIZE
11462306a36Sopenharmony_ci#define SS_BULK_IN_DELAY		0x2000
11562306a36Sopenharmony_ci#define HS_BURST_CAP_SIZE		RX_HS_URB_SIZE
11662306a36Sopenharmony_ci#define HS_BULK_IN_DELAY		0x2000
11762306a36Sopenharmony_ci#define FS_BURST_CAP_SIZE		RX_FS_URB_SIZE
11862306a36Sopenharmony_ci#define FS_BULK_IN_DELAY		0x2000
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define TX_CMD_LEN			8
12162306a36Sopenharmony_ci#define TX_SKB_MIN_LEN			(TX_CMD_LEN + ETH_HLEN)
12262306a36Sopenharmony_ci#define LAN78XX_TSO_SIZE(dev)		((dev)->tx_urb_size - TX_SKB_MIN_LEN)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define RX_CMD_LEN			10
12562306a36Sopenharmony_ci#define RX_SKB_MIN_LEN			(RX_CMD_LEN + ETH_HLEN)
12662306a36Sopenharmony_ci#define RX_MAX_FRAME_LEN(mtu)		((mtu) + ETH_HLEN + VLAN_HLEN)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* USB related defines */
12962306a36Sopenharmony_ci#define BULK_IN_PIPE			1
13062306a36Sopenharmony_ci#define BULK_OUT_PIPE			2
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* default autosuspend delay (mSec)*/
13362306a36Sopenharmony_ci#define DEFAULT_AUTOSUSPEND_DELAY	(10 * 1000)
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* statistic update interval (mSec) */
13662306a36Sopenharmony_ci#define STAT_UPDATE_TIMER		(1 * 1000)
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* time to wait for MAC or FCT to stop (jiffies) */
13962306a36Sopenharmony_ci#define HW_DISABLE_TIMEOUT		(HZ / 10)
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* time to wait between polling MAC or FCT state (ms) */
14262306a36Sopenharmony_ci#define HW_DISABLE_DELAY_MS		1
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* defines interrupts from interrupt EP */
14562306a36Sopenharmony_ci#define MAX_INT_EP			(32)
14662306a36Sopenharmony_ci#define INT_EP_INTEP			(31)
14762306a36Sopenharmony_ci#define INT_EP_OTP_WR_DONE		(28)
14862306a36Sopenharmony_ci#define INT_EP_EEE_TX_LPI_START		(26)
14962306a36Sopenharmony_ci#define INT_EP_EEE_TX_LPI_STOP		(25)
15062306a36Sopenharmony_ci#define INT_EP_EEE_RX_LPI		(24)
15162306a36Sopenharmony_ci#define INT_EP_MAC_RESET_TIMEOUT	(23)
15262306a36Sopenharmony_ci#define INT_EP_RDFO			(22)
15362306a36Sopenharmony_ci#define INT_EP_TXE			(21)
15462306a36Sopenharmony_ci#define INT_EP_USB_STATUS		(20)
15562306a36Sopenharmony_ci#define INT_EP_TX_DIS			(19)
15662306a36Sopenharmony_ci#define INT_EP_RX_DIS			(18)
15762306a36Sopenharmony_ci#define INT_EP_PHY			(17)
15862306a36Sopenharmony_ci#define INT_EP_DP			(16)
15962306a36Sopenharmony_ci#define INT_EP_MAC_ERR			(15)
16062306a36Sopenharmony_ci#define INT_EP_TDFU			(14)
16162306a36Sopenharmony_ci#define INT_EP_TDFO			(13)
16262306a36Sopenharmony_ci#define INT_EP_UTX			(12)
16362306a36Sopenharmony_ci#define INT_EP_GPIO_11			(11)
16462306a36Sopenharmony_ci#define INT_EP_GPIO_10			(10)
16562306a36Sopenharmony_ci#define INT_EP_GPIO_9			(9)
16662306a36Sopenharmony_ci#define INT_EP_GPIO_8			(8)
16762306a36Sopenharmony_ci#define INT_EP_GPIO_7			(7)
16862306a36Sopenharmony_ci#define INT_EP_GPIO_6			(6)
16962306a36Sopenharmony_ci#define INT_EP_GPIO_5			(5)
17062306a36Sopenharmony_ci#define INT_EP_GPIO_4			(4)
17162306a36Sopenharmony_ci#define INT_EP_GPIO_3			(3)
17262306a36Sopenharmony_ci#define INT_EP_GPIO_2			(2)
17362306a36Sopenharmony_ci#define INT_EP_GPIO_1			(1)
17462306a36Sopenharmony_ci#define INT_EP_GPIO_0			(0)
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const char lan78xx_gstrings[][ETH_GSTRING_LEN] = {
17762306a36Sopenharmony_ci	"RX FCS Errors",
17862306a36Sopenharmony_ci	"RX Alignment Errors",
17962306a36Sopenharmony_ci	"Rx Fragment Errors",
18062306a36Sopenharmony_ci	"RX Jabber Errors",
18162306a36Sopenharmony_ci	"RX Undersize Frame Errors",
18262306a36Sopenharmony_ci	"RX Oversize Frame Errors",
18362306a36Sopenharmony_ci	"RX Dropped Frames",
18462306a36Sopenharmony_ci	"RX Unicast Byte Count",
18562306a36Sopenharmony_ci	"RX Broadcast Byte Count",
18662306a36Sopenharmony_ci	"RX Multicast Byte Count",
18762306a36Sopenharmony_ci	"RX Unicast Frames",
18862306a36Sopenharmony_ci	"RX Broadcast Frames",
18962306a36Sopenharmony_ci	"RX Multicast Frames",
19062306a36Sopenharmony_ci	"RX Pause Frames",
19162306a36Sopenharmony_ci	"RX 64 Byte Frames",
19262306a36Sopenharmony_ci	"RX 65 - 127 Byte Frames",
19362306a36Sopenharmony_ci	"RX 128 - 255 Byte Frames",
19462306a36Sopenharmony_ci	"RX 256 - 511 Bytes Frames",
19562306a36Sopenharmony_ci	"RX 512 - 1023 Byte Frames",
19662306a36Sopenharmony_ci	"RX 1024 - 1518 Byte Frames",
19762306a36Sopenharmony_ci	"RX Greater 1518 Byte Frames",
19862306a36Sopenharmony_ci	"EEE RX LPI Transitions",
19962306a36Sopenharmony_ci	"EEE RX LPI Time",
20062306a36Sopenharmony_ci	"TX FCS Errors",
20162306a36Sopenharmony_ci	"TX Excess Deferral Errors",
20262306a36Sopenharmony_ci	"TX Carrier Errors",
20362306a36Sopenharmony_ci	"TX Bad Byte Count",
20462306a36Sopenharmony_ci	"TX Single Collisions",
20562306a36Sopenharmony_ci	"TX Multiple Collisions",
20662306a36Sopenharmony_ci	"TX Excessive Collision",
20762306a36Sopenharmony_ci	"TX Late Collisions",
20862306a36Sopenharmony_ci	"TX Unicast Byte Count",
20962306a36Sopenharmony_ci	"TX Broadcast Byte Count",
21062306a36Sopenharmony_ci	"TX Multicast Byte Count",
21162306a36Sopenharmony_ci	"TX Unicast Frames",
21262306a36Sopenharmony_ci	"TX Broadcast Frames",
21362306a36Sopenharmony_ci	"TX Multicast Frames",
21462306a36Sopenharmony_ci	"TX Pause Frames",
21562306a36Sopenharmony_ci	"TX 64 Byte Frames",
21662306a36Sopenharmony_ci	"TX 65 - 127 Byte Frames",
21762306a36Sopenharmony_ci	"TX 128 - 255 Byte Frames",
21862306a36Sopenharmony_ci	"TX 256 - 511 Bytes Frames",
21962306a36Sopenharmony_ci	"TX 512 - 1023 Byte Frames",
22062306a36Sopenharmony_ci	"TX 1024 - 1518 Byte Frames",
22162306a36Sopenharmony_ci	"TX Greater 1518 Byte Frames",
22262306a36Sopenharmony_ci	"EEE TX LPI Transitions",
22362306a36Sopenharmony_ci	"EEE TX LPI Time",
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistruct lan78xx_statstage {
22762306a36Sopenharmony_ci	u32 rx_fcs_errors;
22862306a36Sopenharmony_ci	u32 rx_alignment_errors;
22962306a36Sopenharmony_ci	u32 rx_fragment_errors;
23062306a36Sopenharmony_ci	u32 rx_jabber_errors;
23162306a36Sopenharmony_ci	u32 rx_undersize_frame_errors;
23262306a36Sopenharmony_ci	u32 rx_oversize_frame_errors;
23362306a36Sopenharmony_ci	u32 rx_dropped_frames;
23462306a36Sopenharmony_ci	u32 rx_unicast_byte_count;
23562306a36Sopenharmony_ci	u32 rx_broadcast_byte_count;
23662306a36Sopenharmony_ci	u32 rx_multicast_byte_count;
23762306a36Sopenharmony_ci	u32 rx_unicast_frames;
23862306a36Sopenharmony_ci	u32 rx_broadcast_frames;
23962306a36Sopenharmony_ci	u32 rx_multicast_frames;
24062306a36Sopenharmony_ci	u32 rx_pause_frames;
24162306a36Sopenharmony_ci	u32 rx_64_byte_frames;
24262306a36Sopenharmony_ci	u32 rx_65_127_byte_frames;
24362306a36Sopenharmony_ci	u32 rx_128_255_byte_frames;
24462306a36Sopenharmony_ci	u32 rx_256_511_bytes_frames;
24562306a36Sopenharmony_ci	u32 rx_512_1023_byte_frames;
24662306a36Sopenharmony_ci	u32 rx_1024_1518_byte_frames;
24762306a36Sopenharmony_ci	u32 rx_greater_1518_byte_frames;
24862306a36Sopenharmony_ci	u32 eee_rx_lpi_transitions;
24962306a36Sopenharmony_ci	u32 eee_rx_lpi_time;
25062306a36Sopenharmony_ci	u32 tx_fcs_errors;
25162306a36Sopenharmony_ci	u32 tx_excess_deferral_errors;
25262306a36Sopenharmony_ci	u32 tx_carrier_errors;
25362306a36Sopenharmony_ci	u32 tx_bad_byte_count;
25462306a36Sopenharmony_ci	u32 tx_single_collisions;
25562306a36Sopenharmony_ci	u32 tx_multiple_collisions;
25662306a36Sopenharmony_ci	u32 tx_excessive_collision;
25762306a36Sopenharmony_ci	u32 tx_late_collisions;
25862306a36Sopenharmony_ci	u32 tx_unicast_byte_count;
25962306a36Sopenharmony_ci	u32 tx_broadcast_byte_count;
26062306a36Sopenharmony_ci	u32 tx_multicast_byte_count;
26162306a36Sopenharmony_ci	u32 tx_unicast_frames;
26262306a36Sopenharmony_ci	u32 tx_broadcast_frames;
26362306a36Sopenharmony_ci	u32 tx_multicast_frames;
26462306a36Sopenharmony_ci	u32 tx_pause_frames;
26562306a36Sopenharmony_ci	u32 tx_64_byte_frames;
26662306a36Sopenharmony_ci	u32 tx_65_127_byte_frames;
26762306a36Sopenharmony_ci	u32 tx_128_255_byte_frames;
26862306a36Sopenharmony_ci	u32 tx_256_511_bytes_frames;
26962306a36Sopenharmony_ci	u32 tx_512_1023_byte_frames;
27062306a36Sopenharmony_ci	u32 tx_1024_1518_byte_frames;
27162306a36Sopenharmony_ci	u32 tx_greater_1518_byte_frames;
27262306a36Sopenharmony_ci	u32 eee_tx_lpi_transitions;
27362306a36Sopenharmony_ci	u32 eee_tx_lpi_time;
27462306a36Sopenharmony_ci};
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistruct lan78xx_statstage64 {
27762306a36Sopenharmony_ci	u64 rx_fcs_errors;
27862306a36Sopenharmony_ci	u64 rx_alignment_errors;
27962306a36Sopenharmony_ci	u64 rx_fragment_errors;
28062306a36Sopenharmony_ci	u64 rx_jabber_errors;
28162306a36Sopenharmony_ci	u64 rx_undersize_frame_errors;
28262306a36Sopenharmony_ci	u64 rx_oversize_frame_errors;
28362306a36Sopenharmony_ci	u64 rx_dropped_frames;
28462306a36Sopenharmony_ci	u64 rx_unicast_byte_count;
28562306a36Sopenharmony_ci	u64 rx_broadcast_byte_count;
28662306a36Sopenharmony_ci	u64 rx_multicast_byte_count;
28762306a36Sopenharmony_ci	u64 rx_unicast_frames;
28862306a36Sopenharmony_ci	u64 rx_broadcast_frames;
28962306a36Sopenharmony_ci	u64 rx_multicast_frames;
29062306a36Sopenharmony_ci	u64 rx_pause_frames;
29162306a36Sopenharmony_ci	u64 rx_64_byte_frames;
29262306a36Sopenharmony_ci	u64 rx_65_127_byte_frames;
29362306a36Sopenharmony_ci	u64 rx_128_255_byte_frames;
29462306a36Sopenharmony_ci	u64 rx_256_511_bytes_frames;
29562306a36Sopenharmony_ci	u64 rx_512_1023_byte_frames;
29662306a36Sopenharmony_ci	u64 rx_1024_1518_byte_frames;
29762306a36Sopenharmony_ci	u64 rx_greater_1518_byte_frames;
29862306a36Sopenharmony_ci	u64 eee_rx_lpi_transitions;
29962306a36Sopenharmony_ci	u64 eee_rx_lpi_time;
30062306a36Sopenharmony_ci	u64 tx_fcs_errors;
30162306a36Sopenharmony_ci	u64 tx_excess_deferral_errors;
30262306a36Sopenharmony_ci	u64 tx_carrier_errors;
30362306a36Sopenharmony_ci	u64 tx_bad_byte_count;
30462306a36Sopenharmony_ci	u64 tx_single_collisions;
30562306a36Sopenharmony_ci	u64 tx_multiple_collisions;
30662306a36Sopenharmony_ci	u64 tx_excessive_collision;
30762306a36Sopenharmony_ci	u64 tx_late_collisions;
30862306a36Sopenharmony_ci	u64 tx_unicast_byte_count;
30962306a36Sopenharmony_ci	u64 tx_broadcast_byte_count;
31062306a36Sopenharmony_ci	u64 tx_multicast_byte_count;
31162306a36Sopenharmony_ci	u64 tx_unicast_frames;
31262306a36Sopenharmony_ci	u64 tx_broadcast_frames;
31362306a36Sopenharmony_ci	u64 tx_multicast_frames;
31462306a36Sopenharmony_ci	u64 tx_pause_frames;
31562306a36Sopenharmony_ci	u64 tx_64_byte_frames;
31662306a36Sopenharmony_ci	u64 tx_65_127_byte_frames;
31762306a36Sopenharmony_ci	u64 tx_128_255_byte_frames;
31862306a36Sopenharmony_ci	u64 tx_256_511_bytes_frames;
31962306a36Sopenharmony_ci	u64 tx_512_1023_byte_frames;
32062306a36Sopenharmony_ci	u64 tx_1024_1518_byte_frames;
32162306a36Sopenharmony_ci	u64 tx_greater_1518_byte_frames;
32262306a36Sopenharmony_ci	u64 eee_tx_lpi_transitions;
32362306a36Sopenharmony_ci	u64 eee_tx_lpi_time;
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic u32 lan78xx_regs[] = {
32762306a36Sopenharmony_ci	ID_REV,
32862306a36Sopenharmony_ci	INT_STS,
32962306a36Sopenharmony_ci	HW_CFG,
33062306a36Sopenharmony_ci	PMT_CTL,
33162306a36Sopenharmony_ci	E2P_CMD,
33262306a36Sopenharmony_ci	E2P_DATA,
33362306a36Sopenharmony_ci	USB_STATUS,
33462306a36Sopenharmony_ci	VLAN_TYPE,
33562306a36Sopenharmony_ci	MAC_CR,
33662306a36Sopenharmony_ci	MAC_RX,
33762306a36Sopenharmony_ci	MAC_TX,
33862306a36Sopenharmony_ci	FLOW,
33962306a36Sopenharmony_ci	ERR_STS,
34062306a36Sopenharmony_ci	MII_ACC,
34162306a36Sopenharmony_ci	MII_DATA,
34262306a36Sopenharmony_ci	EEE_TX_LPI_REQ_DLY,
34362306a36Sopenharmony_ci	EEE_TW_TX_SYS,
34462306a36Sopenharmony_ci	EEE_TX_LPI_REM_DLY,
34562306a36Sopenharmony_ci	WUCSR
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci#define PHY_REG_SIZE (32 * sizeof(u32))
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistruct lan78xx_net;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistruct lan78xx_priv {
35362306a36Sopenharmony_ci	struct lan78xx_net *dev;
35462306a36Sopenharmony_ci	u32 rfe_ctl;
35562306a36Sopenharmony_ci	u32 mchash_table[DP_SEL_VHF_HASH_LEN]; /* multicast hash table */
35662306a36Sopenharmony_ci	u32 pfilter_table[NUM_OF_MAF][2]; /* perfect filter table */
35762306a36Sopenharmony_ci	u32 vlan_table[DP_SEL_VHF_VLAN_LEN];
35862306a36Sopenharmony_ci	struct mutex dataport_mutex; /* for dataport access */
35962306a36Sopenharmony_ci	spinlock_t rfe_ctl_lock; /* for rfe register access */
36062306a36Sopenharmony_ci	struct work_struct set_multicast;
36162306a36Sopenharmony_ci	struct work_struct set_vlan;
36262306a36Sopenharmony_ci	u32 wol;
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cienum skb_state {
36662306a36Sopenharmony_ci	illegal = 0,
36762306a36Sopenharmony_ci	tx_start,
36862306a36Sopenharmony_ci	tx_done,
36962306a36Sopenharmony_ci	rx_start,
37062306a36Sopenharmony_ci	rx_done,
37162306a36Sopenharmony_ci	rx_cleanup,
37262306a36Sopenharmony_ci	unlink_start
37362306a36Sopenharmony_ci};
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistruct skb_data {		/* skb->cb is one of these */
37662306a36Sopenharmony_ci	struct urb *urb;
37762306a36Sopenharmony_ci	struct lan78xx_net *dev;
37862306a36Sopenharmony_ci	enum skb_state state;
37962306a36Sopenharmony_ci	size_t length;
38062306a36Sopenharmony_ci	int num_of_packet;
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistruct usb_context {
38462306a36Sopenharmony_ci	struct usb_ctrlrequest req;
38562306a36Sopenharmony_ci	struct lan78xx_net *dev;
38662306a36Sopenharmony_ci};
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci#define EVENT_TX_HALT			0
38962306a36Sopenharmony_ci#define EVENT_RX_HALT			1
39062306a36Sopenharmony_ci#define EVENT_RX_MEMORY			2
39162306a36Sopenharmony_ci#define EVENT_STS_SPLIT			3
39262306a36Sopenharmony_ci#define EVENT_LINK_RESET		4
39362306a36Sopenharmony_ci#define EVENT_RX_PAUSED			5
39462306a36Sopenharmony_ci#define EVENT_DEV_WAKING		6
39562306a36Sopenharmony_ci#define EVENT_DEV_ASLEEP		7
39662306a36Sopenharmony_ci#define EVENT_DEV_OPEN			8
39762306a36Sopenharmony_ci#define EVENT_STAT_UPDATE		9
39862306a36Sopenharmony_ci#define EVENT_DEV_DISCONNECT		10
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistruct statstage {
40162306a36Sopenharmony_ci	struct mutex			access_lock;	/* for stats access */
40262306a36Sopenharmony_ci	struct lan78xx_statstage	saved;
40362306a36Sopenharmony_ci	struct lan78xx_statstage	rollover_count;
40462306a36Sopenharmony_ci	struct lan78xx_statstage	rollover_max;
40562306a36Sopenharmony_ci	struct lan78xx_statstage64	curr_stat;
40662306a36Sopenharmony_ci};
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistruct irq_domain_data {
40962306a36Sopenharmony_ci	struct irq_domain	*irqdomain;
41062306a36Sopenharmony_ci	unsigned int		phyirq;
41162306a36Sopenharmony_ci	struct irq_chip		*irqchip;
41262306a36Sopenharmony_ci	irq_flow_handler_t	irq_handler;
41362306a36Sopenharmony_ci	u32			irqenable;
41462306a36Sopenharmony_ci	struct mutex		irq_lock;		/* for irq bus access */
41562306a36Sopenharmony_ci};
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistruct lan78xx_net {
41862306a36Sopenharmony_ci	struct net_device	*net;
41962306a36Sopenharmony_ci	struct usb_device	*udev;
42062306a36Sopenharmony_ci	struct usb_interface	*intf;
42162306a36Sopenharmony_ci	void			*driver_priv;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	unsigned int		tx_pend_data_len;
42462306a36Sopenharmony_ci	size_t			n_tx_urbs;
42562306a36Sopenharmony_ci	size_t			n_rx_urbs;
42662306a36Sopenharmony_ci	size_t			tx_urb_size;
42762306a36Sopenharmony_ci	size_t			rx_urb_size;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	struct sk_buff_head	rxq_free;
43062306a36Sopenharmony_ci	struct sk_buff_head	rxq;
43162306a36Sopenharmony_ci	struct sk_buff_head	rxq_done;
43262306a36Sopenharmony_ci	struct sk_buff_head	rxq_overflow;
43362306a36Sopenharmony_ci	struct sk_buff_head	txq_free;
43462306a36Sopenharmony_ci	struct sk_buff_head	txq;
43562306a36Sopenharmony_ci	struct sk_buff_head	txq_pend;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	struct napi_struct	napi;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	struct delayed_work	wq;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	int			msg_enable;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	struct urb		*urb_intr;
44462306a36Sopenharmony_ci	struct usb_anchor	deferred;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	struct mutex		dev_mutex; /* serialise open/stop wrt suspend/resume */
44762306a36Sopenharmony_ci	struct mutex		phy_mutex; /* for phy access */
44862306a36Sopenharmony_ci	unsigned int		pipe_in, pipe_out, pipe_intr;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	unsigned int		bulk_in_delay;
45162306a36Sopenharmony_ci	unsigned int		burst_cap;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	unsigned long		flags;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	wait_queue_head_t	*wait;
45662306a36Sopenharmony_ci	unsigned char		suspend_count;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	unsigned int		maxpacket;
45962306a36Sopenharmony_ci	struct timer_list	stat_monitor;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	unsigned long		data[5];
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	int			link_on;
46462306a36Sopenharmony_ci	u8			mdix_ctrl;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	u32			chipid;
46762306a36Sopenharmony_ci	u32			chiprev;
46862306a36Sopenharmony_ci	struct mii_bus		*mdiobus;
46962306a36Sopenharmony_ci	phy_interface_t		interface;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	int			fc_autoneg;
47262306a36Sopenharmony_ci	u8			fc_request_control;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	int			delta;
47562306a36Sopenharmony_ci	struct statstage	stats;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	struct irq_domain_data	domain_data;
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/* define external phy id */
48162306a36Sopenharmony_ci#define	PHY_LAN8835			(0x0007C130)
48262306a36Sopenharmony_ci#define	PHY_KSZ9031RNX			(0x00221620)
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci/* use ethtool to change the level for any given device */
48562306a36Sopenharmony_cistatic int msg_level = -1;
48662306a36Sopenharmony_cimodule_param(msg_level, int, 0);
48762306a36Sopenharmony_ciMODULE_PARM_DESC(msg_level, "Override default message level");
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic struct sk_buff *lan78xx_get_buf(struct sk_buff_head *buf_pool)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	if (skb_queue_empty(buf_pool))
49262306a36Sopenharmony_ci		return NULL;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return skb_dequeue(buf_pool);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic void lan78xx_release_buf(struct sk_buff_head *buf_pool,
49862306a36Sopenharmony_ci				struct sk_buff *buf)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	buf->data = buf->head;
50162306a36Sopenharmony_ci	skb_reset_tail_pointer(buf);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	buf->len = 0;
50462306a36Sopenharmony_ci	buf->data_len = 0;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	skb_queue_tail(buf_pool, buf);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void lan78xx_free_buf_pool(struct sk_buff_head *buf_pool)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct skb_data *entry;
51262306a36Sopenharmony_ci	struct sk_buff *buf;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	while (!skb_queue_empty(buf_pool)) {
51562306a36Sopenharmony_ci		buf = skb_dequeue(buf_pool);
51662306a36Sopenharmony_ci		if (buf) {
51762306a36Sopenharmony_ci			entry = (struct skb_data *)buf->cb;
51862306a36Sopenharmony_ci			usb_free_urb(entry->urb);
51962306a36Sopenharmony_ci			dev_kfree_skb_any(buf);
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int lan78xx_alloc_buf_pool(struct sk_buff_head *buf_pool,
52562306a36Sopenharmony_ci				  size_t n_urbs, size_t urb_size,
52662306a36Sopenharmony_ci				  struct lan78xx_net *dev)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct skb_data *entry;
52962306a36Sopenharmony_ci	struct sk_buff *buf;
53062306a36Sopenharmony_ci	struct urb *urb;
53162306a36Sopenharmony_ci	int i;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	skb_queue_head_init(buf_pool);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	for (i = 0; i < n_urbs; i++) {
53662306a36Sopenharmony_ci		buf = alloc_skb(urb_size, GFP_ATOMIC);
53762306a36Sopenharmony_ci		if (!buf)
53862306a36Sopenharmony_ci			goto error;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		if (skb_linearize(buf) != 0) {
54162306a36Sopenharmony_ci			dev_kfree_skb_any(buf);
54262306a36Sopenharmony_ci			goto error;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_ATOMIC);
54662306a36Sopenharmony_ci		if (!urb) {
54762306a36Sopenharmony_ci			dev_kfree_skb_any(buf);
54862306a36Sopenharmony_ci			goto error;
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		entry = (struct skb_data *)buf->cb;
55262306a36Sopenharmony_ci		entry->urb = urb;
55362306a36Sopenharmony_ci		entry->dev = dev;
55462306a36Sopenharmony_ci		entry->length = 0;
55562306a36Sopenharmony_ci		entry->num_of_packet = 0;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		skb_queue_tail(buf_pool, buf);
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cierror:
56362306a36Sopenharmony_ci	lan78xx_free_buf_pool(buf_pool);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return -ENOMEM;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic struct sk_buff *lan78xx_get_rx_buf(struct lan78xx_net *dev)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	return lan78xx_get_buf(&dev->rxq_free);
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void lan78xx_release_rx_buf(struct lan78xx_net *dev,
57462306a36Sopenharmony_ci				   struct sk_buff *rx_buf)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	lan78xx_release_buf(&dev->rxq_free, rx_buf);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic void lan78xx_free_rx_resources(struct lan78xx_net *dev)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	lan78xx_free_buf_pool(&dev->rxq_free);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int lan78xx_alloc_rx_resources(struct lan78xx_net *dev)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	return lan78xx_alloc_buf_pool(&dev->rxq_free,
58762306a36Sopenharmony_ci				      dev->n_rx_urbs, dev->rx_urb_size, dev);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic struct sk_buff *lan78xx_get_tx_buf(struct lan78xx_net *dev)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	return lan78xx_get_buf(&dev->txq_free);
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void lan78xx_release_tx_buf(struct lan78xx_net *dev,
59662306a36Sopenharmony_ci				   struct sk_buff *tx_buf)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	lan78xx_release_buf(&dev->txq_free, tx_buf);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void lan78xx_free_tx_resources(struct lan78xx_net *dev)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	lan78xx_free_buf_pool(&dev->txq_free);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int lan78xx_alloc_tx_resources(struct lan78xx_net *dev)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	return lan78xx_alloc_buf_pool(&dev->txq_free,
60962306a36Sopenharmony_ci				      dev->n_tx_urbs, dev->tx_urb_size, dev);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	u32 *buf;
61562306a36Sopenharmony_ci	int ret;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags))
61862306a36Sopenharmony_ci		return -ENODEV;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	buf = kmalloc(sizeof(u32), GFP_KERNEL);
62162306a36Sopenharmony_ci	if (!buf)
62262306a36Sopenharmony_ci		return -ENOMEM;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
62562306a36Sopenharmony_ci			      USB_VENDOR_REQUEST_READ_REGISTER,
62662306a36Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
62762306a36Sopenharmony_ci			      0, index, buf, 4, USB_CTRL_GET_TIMEOUT);
62862306a36Sopenharmony_ci	if (likely(ret >= 0)) {
62962306a36Sopenharmony_ci		le32_to_cpus(buf);
63062306a36Sopenharmony_ci		*data = *buf;
63162306a36Sopenharmony_ci	} else if (net_ratelimit()) {
63262306a36Sopenharmony_ci		netdev_warn(dev->net,
63362306a36Sopenharmony_ci			    "Failed to read register index 0x%08x. ret = %d",
63462306a36Sopenharmony_ci			    index, ret);
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	kfree(buf);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return ret;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int lan78xx_write_reg(struct lan78xx_net *dev, u32 index, u32 data)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	u32 *buf;
64562306a36Sopenharmony_ci	int ret;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags))
64862306a36Sopenharmony_ci		return -ENODEV;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	buf = kmalloc(sizeof(u32), GFP_KERNEL);
65162306a36Sopenharmony_ci	if (!buf)
65262306a36Sopenharmony_ci		return -ENOMEM;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	*buf = data;
65562306a36Sopenharmony_ci	cpu_to_le32s(buf);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
65862306a36Sopenharmony_ci			      USB_VENDOR_REQUEST_WRITE_REGISTER,
65962306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
66062306a36Sopenharmony_ci			      0, index, buf, 4, USB_CTRL_SET_TIMEOUT);
66162306a36Sopenharmony_ci	if (unlikely(ret < 0) &&
66262306a36Sopenharmony_ci	    net_ratelimit()) {
66362306a36Sopenharmony_ci		netdev_warn(dev->net,
66462306a36Sopenharmony_ci			    "Failed to write register index 0x%08x. ret = %d",
66562306a36Sopenharmony_ci			    index, ret);
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	kfree(buf);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	return ret;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic int lan78xx_update_reg(struct lan78xx_net *dev, u32 reg, u32 mask,
67462306a36Sopenharmony_ci			      u32 data)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	int ret;
67762306a36Sopenharmony_ci	u32 buf;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, reg, &buf);
68062306a36Sopenharmony_ci	if (ret < 0)
68162306a36Sopenharmony_ci		return ret;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	buf &= ~mask;
68462306a36Sopenharmony_ci	buf |= (mask & data);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, reg, buf);
68762306a36Sopenharmony_ci	if (ret < 0)
68862306a36Sopenharmony_ci		return ret;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int lan78xx_read_stats(struct lan78xx_net *dev,
69462306a36Sopenharmony_ci			      struct lan78xx_statstage *data)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	int ret = 0;
69762306a36Sopenharmony_ci	int i;
69862306a36Sopenharmony_ci	struct lan78xx_statstage *stats;
69962306a36Sopenharmony_ci	u32 *src;
70062306a36Sopenharmony_ci	u32 *dst;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	stats = kmalloc(sizeof(*stats), GFP_KERNEL);
70362306a36Sopenharmony_ci	if (!stats)
70462306a36Sopenharmony_ci		return -ENOMEM;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	ret = usb_control_msg(dev->udev,
70762306a36Sopenharmony_ci			      usb_rcvctrlpipe(dev->udev, 0),
70862306a36Sopenharmony_ci			      USB_VENDOR_REQUEST_GET_STATS,
70962306a36Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
71062306a36Sopenharmony_ci			      0,
71162306a36Sopenharmony_ci			      0,
71262306a36Sopenharmony_ci			      (void *)stats,
71362306a36Sopenharmony_ci			      sizeof(*stats),
71462306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
71562306a36Sopenharmony_ci	if (likely(ret >= 0)) {
71662306a36Sopenharmony_ci		src = (u32 *)stats;
71762306a36Sopenharmony_ci		dst = (u32 *)data;
71862306a36Sopenharmony_ci		for (i = 0; i < sizeof(*stats) / sizeof(u32); i++) {
71962306a36Sopenharmony_ci			le32_to_cpus(&src[i]);
72062306a36Sopenharmony_ci			dst[i] = src[i];
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci	} else {
72362306a36Sopenharmony_ci		netdev_warn(dev->net,
72462306a36Sopenharmony_ci			    "Failed to read stat ret = %d", ret);
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	kfree(stats);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return ret;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci#define check_counter_rollover(struct1, dev_stats, member)		\
73362306a36Sopenharmony_ci	do {								\
73462306a36Sopenharmony_ci		if ((struct1)->member < (dev_stats).saved.member)	\
73562306a36Sopenharmony_ci			(dev_stats).rollover_count.member++;		\
73662306a36Sopenharmony_ci	} while (0)
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic void lan78xx_check_stat_rollover(struct lan78xx_net *dev,
73962306a36Sopenharmony_ci					struct lan78xx_statstage *stats)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_fcs_errors);
74262306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_alignment_errors);
74362306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_fragment_errors);
74462306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_jabber_errors);
74562306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_undersize_frame_errors);
74662306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_oversize_frame_errors);
74762306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_dropped_frames);
74862306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_unicast_byte_count);
74962306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_broadcast_byte_count);
75062306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_multicast_byte_count);
75162306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_unicast_frames);
75262306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_broadcast_frames);
75362306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_multicast_frames);
75462306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_pause_frames);
75562306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_64_byte_frames);
75662306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_65_127_byte_frames);
75762306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_128_255_byte_frames);
75862306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_256_511_bytes_frames);
75962306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_512_1023_byte_frames);
76062306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_1024_1518_byte_frames);
76162306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, rx_greater_1518_byte_frames);
76262306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, eee_rx_lpi_transitions);
76362306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, eee_rx_lpi_time);
76462306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_fcs_errors);
76562306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_excess_deferral_errors);
76662306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_carrier_errors);
76762306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_bad_byte_count);
76862306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_single_collisions);
76962306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_multiple_collisions);
77062306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_excessive_collision);
77162306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_late_collisions);
77262306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_unicast_byte_count);
77362306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_broadcast_byte_count);
77462306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_multicast_byte_count);
77562306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_unicast_frames);
77662306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_broadcast_frames);
77762306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_multicast_frames);
77862306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_pause_frames);
77962306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_64_byte_frames);
78062306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_65_127_byte_frames);
78162306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_128_255_byte_frames);
78262306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_256_511_bytes_frames);
78362306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_512_1023_byte_frames);
78462306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_1024_1518_byte_frames);
78562306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, tx_greater_1518_byte_frames);
78662306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, eee_tx_lpi_transitions);
78762306a36Sopenharmony_ci	check_counter_rollover(stats, dev->stats, eee_tx_lpi_time);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	memcpy(&dev->stats.saved, stats, sizeof(struct lan78xx_statstage));
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void lan78xx_update_stats(struct lan78xx_net *dev)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	u32 *p, *count, *max;
79562306a36Sopenharmony_ci	u64 *data;
79662306a36Sopenharmony_ci	int i;
79762306a36Sopenharmony_ci	struct lan78xx_statstage lan78xx_stats;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (usb_autopm_get_interface(dev->intf) < 0)
80062306a36Sopenharmony_ci		return;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	p = (u32 *)&lan78xx_stats;
80362306a36Sopenharmony_ci	count = (u32 *)&dev->stats.rollover_count;
80462306a36Sopenharmony_ci	max = (u32 *)&dev->stats.rollover_max;
80562306a36Sopenharmony_ci	data = (u64 *)&dev->stats.curr_stat;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	mutex_lock(&dev->stats.access_lock);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (lan78xx_read_stats(dev, &lan78xx_stats) > 0)
81062306a36Sopenharmony_ci		lan78xx_check_stat_rollover(dev, &lan78xx_stats);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	for (i = 0; i < (sizeof(lan78xx_stats) / (sizeof(u32))); i++)
81362306a36Sopenharmony_ci		data[i] = (u64)p[i] + ((u64)count[i] * ((u64)max[i] + 1));
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	mutex_unlock(&dev->stats.access_lock);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/* Loop until the read is completed with timeout called with phy_mutex held */
82162306a36Sopenharmony_cistatic int lan78xx_phy_wait_not_busy(struct lan78xx_net *dev)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	unsigned long start_time = jiffies;
82462306a36Sopenharmony_ci	u32 val;
82562306a36Sopenharmony_ci	int ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	do {
82862306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, MII_ACC, &val);
82962306a36Sopenharmony_ci		if (unlikely(ret < 0))
83062306a36Sopenharmony_ci			return -EIO;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		if (!(val & MII_ACC_MII_BUSY_))
83362306a36Sopenharmony_ci			return 0;
83462306a36Sopenharmony_ci	} while (!time_after(jiffies, start_time + HZ));
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return -EIO;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic inline u32 mii_access(int id, int index, int read)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	u32 ret;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	ret = ((u32)id << MII_ACC_PHY_ADDR_SHIFT_) & MII_ACC_PHY_ADDR_MASK_;
84462306a36Sopenharmony_ci	ret |= ((u32)index << MII_ACC_MIIRINDA_SHIFT_) & MII_ACC_MIIRINDA_MASK_;
84562306a36Sopenharmony_ci	if (read)
84662306a36Sopenharmony_ci		ret |= MII_ACC_MII_READ_;
84762306a36Sopenharmony_ci	else
84862306a36Sopenharmony_ci		ret |= MII_ACC_MII_WRITE_;
84962306a36Sopenharmony_ci	ret |= MII_ACC_MII_BUSY_;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return ret;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic int lan78xx_wait_eeprom(struct lan78xx_net *dev)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	unsigned long start_time = jiffies;
85762306a36Sopenharmony_ci	u32 val;
85862306a36Sopenharmony_ci	int ret;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	do {
86162306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, E2P_CMD, &val);
86262306a36Sopenharmony_ci		if (unlikely(ret < 0))
86362306a36Sopenharmony_ci			return -EIO;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		if (!(val & E2P_CMD_EPC_BUSY_) ||
86662306a36Sopenharmony_ci		    (val & E2P_CMD_EPC_TIMEOUT_))
86762306a36Sopenharmony_ci			break;
86862306a36Sopenharmony_ci		usleep_range(40, 100);
86962306a36Sopenharmony_ci	} while (!time_after(jiffies, start_time + HZ));
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (val & (E2P_CMD_EPC_TIMEOUT_ | E2P_CMD_EPC_BUSY_)) {
87262306a36Sopenharmony_ci		netdev_warn(dev->net, "EEPROM read operation timeout");
87362306a36Sopenharmony_ci		return -EIO;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return 0;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int lan78xx_eeprom_confirm_not_busy(struct lan78xx_net *dev)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	unsigned long start_time = jiffies;
88262306a36Sopenharmony_ci	u32 val;
88362306a36Sopenharmony_ci	int ret;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	do {
88662306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, E2P_CMD, &val);
88762306a36Sopenharmony_ci		if (unlikely(ret < 0))
88862306a36Sopenharmony_ci			return -EIO;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci		if (!(val & E2P_CMD_EPC_BUSY_))
89162306a36Sopenharmony_ci			return 0;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		usleep_range(40, 100);
89462306a36Sopenharmony_ci	} while (!time_after(jiffies, start_time + HZ));
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	netdev_warn(dev->net, "EEPROM is busy");
89762306a36Sopenharmony_ci	return -EIO;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset,
90162306a36Sopenharmony_ci				   u32 length, u8 *data)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	u32 val;
90462306a36Sopenharmony_ci	u32 saved;
90562306a36Sopenharmony_ci	int i, ret;
90662306a36Sopenharmony_ci	int retval;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* depends on chip, some EEPROM pins are muxed with LED function.
90962306a36Sopenharmony_ci	 * disable & restore LED function to access EEPROM.
91062306a36Sopenharmony_ci	 */
91162306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, HW_CFG, &val);
91262306a36Sopenharmony_ci	saved = val;
91362306a36Sopenharmony_ci	if (dev->chipid == ID_REV_CHIP_ID_7800_) {
91462306a36Sopenharmony_ci		val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_);
91562306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, HW_CFG, val);
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	retval = lan78xx_eeprom_confirm_not_busy(dev);
91962306a36Sopenharmony_ci	if (retval)
92062306a36Sopenharmony_ci		return retval;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
92362306a36Sopenharmony_ci		val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
92462306a36Sopenharmony_ci		val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
92562306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, E2P_CMD, val);
92662306a36Sopenharmony_ci		if (unlikely(ret < 0)) {
92762306a36Sopenharmony_ci			retval = -EIO;
92862306a36Sopenharmony_ci			goto exit;
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		retval = lan78xx_wait_eeprom(dev);
93262306a36Sopenharmony_ci		if (retval < 0)
93362306a36Sopenharmony_ci			goto exit;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, E2P_DATA, &val);
93662306a36Sopenharmony_ci		if (unlikely(ret < 0)) {
93762306a36Sopenharmony_ci			retval = -EIO;
93862306a36Sopenharmony_ci			goto exit;
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		data[i] = val & 0xFF;
94262306a36Sopenharmony_ci		offset++;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	retval = 0;
94662306a36Sopenharmony_ciexit:
94762306a36Sopenharmony_ci	if (dev->chipid == ID_REV_CHIP_ID_7800_)
94862306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, HW_CFG, saved);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	return retval;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic int lan78xx_read_eeprom(struct lan78xx_net *dev, u32 offset,
95462306a36Sopenharmony_ci			       u32 length, u8 *data)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	u8 sig;
95762306a36Sopenharmony_ci	int ret;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig);
96062306a36Sopenharmony_ci	if ((ret == 0) && (sig == EEPROM_INDICATOR))
96162306a36Sopenharmony_ci		ret = lan78xx_read_raw_eeprom(dev, offset, length, data);
96262306a36Sopenharmony_ci	else
96362306a36Sopenharmony_ci		ret = -EINVAL;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	return ret;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset,
96962306a36Sopenharmony_ci				    u32 length, u8 *data)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	u32 val;
97262306a36Sopenharmony_ci	u32 saved;
97362306a36Sopenharmony_ci	int i, ret;
97462306a36Sopenharmony_ci	int retval;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* depends on chip, some EEPROM pins are muxed with LED function.
97762306a36Sopenharmony_ci	 * disable & restore LED function to access EEPROM.
97862306a36Sopenharmony_ci	 */
97962306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, HW_CFG, &val);
98062306a36Sopenharmony_ci	saved = val;
98162306a36Sopenharmony_ci	if (dev->chipid == ID_REV_CHIP_ID_7800_) {
98262306a36Sopenharmony_ci		val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_);
98362306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, HW_CFG, val);
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	retval = lan78xx_eeprom_confirm_not_busy(dev);
98762306a36Sopenharmony_ci	if (retval)
98862306a36Sopenharmony_ci		goto exit;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	/* Issue write/erase enable command */
99162306a36Sopenharmony_ci	val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
99262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, E2P_CMD, val);
99362306a36Sopenharmony_ci	if (unlikely(ret < 0)) {
99462306a36Sopenharmony_ci		retval = -EIO;
99562306a36Sopenharmony_ci		goto exit;
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	retval = lan78xx_wait_eeprom(dev);
99962306a36Sopenharmony_ci	if (retval < 0)
100062306a36Sopenharmony_ci		goto exit;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
100362306a36Sopenharmony_ci		/* Fill data register */
100462306a36Sopenharmony_ci		val = data[i];
100562306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, E2P_DATA, val);
100662306a36Sopenharmony_ci		if (ret < 0) {
100762306a36Sopenharmony_ci			retval = -EIO;
100862306a36Sopenharmony_ci			goto exit;
100962306a36Sopenharmony_ci		}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		/* Send "write" command */
101262306a36Sopenharmony_ci		val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
101362306a36Sopenharmony_ci		val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
101462306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, E2P_CMD, val);
101562306a36Sopenharmony_ci		if (ret < 0) {
101662306a36Sopenharmony_ci			retval = -EIO;
101762306a36Sopenharmony_ci			goto exit;
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		retval = lan78xx_wait_eeprom(dev);
102162306a36Sopenharmony_ci		if (retval < 0)
102262306a36Sopenharmony_ci			goto exit;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		offset++;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	retval = 0;
102862306a36Sopenharmony_ciexit:
102962306a36Sopenharmony_ci	if (dev->chipid == ID_REV_CHIP_ID_7800_)
103062306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, HW_CFG, saved);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	return retval;
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_cistatic int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset,
103662306a36Sopenharmony_ci				u32 length, u8 *data)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int i;
103962306a36Sopenharmony_ci	u32 buf;
104062306a36Sopenharmony_ci	unsigned long timeout;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (buf & OTP_PWR_DN_PWRDN_N_) {
104562306a36Sopenharmony_ci		/* clear it and wait to be cleared */
104662306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_PWR_DN, 0);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		timeout = jiffies + HZ;
104962306a36Sopenharmony_ci		do {
105062306a36Sopenharmony_ci			usleep_range(1, 10);
105162306a36Sopenharmony_ci			lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
105262306a36Sopenharmony_ci			if (time_after(jiffies, timeout)) {
105362306a36Sopenharmony_ci				netdev_warn(dev->net,
105462306a36Sopenharmony_ci					    "timeout on OTP_PWR_DN");
105562306a36Sopenharmony_ci				return -EIO;
105662306a36Sopenharmony_ci			}
105762306a36Sopenharmony_ci		} while (buf & OTP_PWR_DN_PWRDN_N_);
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
106162306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_ADDR1,
106262306a36Sopenharmony_ci				  ((offset + i) >> 8) & OTP_ADDR1_15_11);
106362306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_ADDR2,
106462306a36Sopenharmony_ci				  ((offset + i) & OTP_ADDR2_10_3));
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_FUNC_CMD, OTP_FUNC_CMD_READ_);
106762306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci		timeout = jiffies + HZ;
107062306a36Sopenharmony_ci		do {
107162306a36Sopenharmony_ci			udelay(1);
107262306a36Sopenharmony_ci			lan78xx_read_reg(dev, OTP_STATUS, &buf);
107362306a36Sopenharmony_ci			if (time_after(jiffies, timeout)) {
107462306a36Sopenharmony_ci				netdev_warn(dev->net,
107562306a36Sopenharmony_ci					    "timeout on OTP_STATUS");
107662306a36Sopenharmony_ci				return -EIO;
107762306a36Sopenharmony_ci			}
107862306a36Sopenharmony_ci		} while (buf & OTP_STATUS_BUSY_);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		lan78xx_read_reg(dev, OTP_RD_DATA, &buf);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		data[i] = (u8)(buf & 0xFF);
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return 0;
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic int lan78xx_write_raw_otp(struct lan78xx_net *dev, u32 offset,
108962306a36Sopenharmony_ci				 u32 length, u8 *data)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	int i;
109262306a36Sopenharmony_ci	u32 buf;
109362306a36Sopenharmony_ci	unsigned long timeout;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (buf & OTP_PWR_DN_PWRDN_N_) {
109862306a36Sopenharmony_ci		/* clear it and wait to be cleared */
109962306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_PWR_DN, 0);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		timeout = jiffies + HZ;
110262306a36Sopenharmony_ci		do {
110362306a36Sopenharmony_ci			udelay(1);
110462306a36Sopenharmony_ci			lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
110562306a36Sopenharmony_ci			if (time_after(jiffies, timeout)) {
110662306a36Sopenharmony_ci				netdev_warn(dev->net,
110762306a36Sopenharmony_ci					    "timeout on OTP_PWR_DN completion");
110862306a36Sopenharmony_ci				return -EIO;
110962306a36Sopenharmony_ci			}
111062306a36Sopenharmony_ci		} while (buf & OTP_PWR_DN_PWRDN_N_);
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* set to BYTE program mode */
111462306a36Sopenharmony_ci	lan78xx_write_reg(dev, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
111762306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_ADDR1,
111862306a36Sopenharmony_ci				  ((offset + i) >> 8) & OTP_ADDR1_15_11);
111962306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_ADDR2,
112062306a36Sopenharmony_ci				  ((offset + i) & OTP_ADDR2_10_3));
112162306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_PRGM_DATA, data[i]);
112262306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_);
112362306a36Sopenharmony_ci		lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		timeout = jiffies + HZ;
112662306a36Sopenharmony_ci		do {
112762306a36Sopenharmony_ci			udelay(1);
112862306a36Sopenharmony_ci			lan78xx_read_reg(dev, OTP_STATUS, &buf);
112962306a36Sopenharmony_ci			if (time_after(jiffies, timeout)) {
113062306a36Sopenharmony_ci				netdev_warn(dev->net,
113162306a36Sopenharmony_ci					    "Timeout on OTP_STATUS completion");
113262306a36Sopenharmony_ci				return -EIO;
113362306a36Sopenharmony_ci			}
113462306a36Sopenharmony_ci		} while (buf & OTP_STATUS_BUSY_);
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	return 0;
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset,
114162306a36Sopenharmony_ci			    u32 length, u8 *data)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	u8 sig;
114462306a36Sopenharmony_ci	int ret;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	ret = lan78xx_read_raw_otp(dev, 0, 1, &sig);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (ret == 0) {
114962306a36Sopenharmony_ci		if (sig == OTP_INDICATOR_2)
115062306a36Sopenharmony_ci			offset += 0x100;
115162306a36Sopenharmony_ci		else if (sig != OTP_INDICATOR_1)
115262306a36Sopenharmony_ci			ret = -EINVAL;
115362306a36Sopenharmony_ci		if (!ret)
115462306a36Sopenharmony_ci			ret = lan78xx_read_raw_otp(dev, offset, length, data);
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return ret;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic int lan78xx_dataport_wait_not_busy(struct lan78xx_net *dev)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	int i, ret;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	for (i = 0; i < 100; i++) {
116562306a36Sopenharmony_ci		u32 dp_sel;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, DP_SEL, &dp_sel);
116862306a36Sopenharmony_ci		if (unlikely(ret < 0))
116962306a36Sopenharmony_ci			return -EIO;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		if (dp_sel & DP_SEL_DPRDY_)
117262306a36Sopenharmony_ci			return 0;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		usleep_range(40, 100);
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	netdev_warn(dev->net, "%s timed out", __func__);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	return -EIO;
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic int lan78xx_dataport_write(struct lan78xx_net *dev, u32 ram_select,
118362306a36Sopenharmony_ci				  u32 addr, u32 length, u32 *buf)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
118662306a36Sopenharmony_ci	u32 dp_sel;
118762306a36Sopenharmony_ci	int i, ret;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (usb_autopm_get_interface(dev->intf) < 0)
119062306a36Sopenharmony_ci		return 0;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	mutex_lock(&pdata->dataport_mutex);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	ret = lan78xx_dataport_wait_not_busy(dev);
119562306a36Sopenharmony_ci	if (ret < 0)
119662306a36Sopenharmony_ci		goto done;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, DP_SEL, &dp_sel);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	dp_sel &= ~DP_SEL_RSEL_MASK_;
120162306a36Sopenharmony_ci	dp_sel |= ram_select;
120262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, DP_SEL, dp_sel);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
120562306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, DP_ADDR, addr + i);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, DP_DATA, buf[i]);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, DP_CMD, DP_CMD_WRITE_);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		ret = lan78xx_dataport_wait_not_busy(dev);
121262306a36Sopenharmony_ci		if (ret < 0)
121362306a36Sopenharmony_ci			goto done;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cidone:
121762306a36Sopenharmony_ci	mutex_unlock(&pdata->dataport_mutex);
121862306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	return ret;
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic void lan78xx_set_addr_filter(struct lan78xx_priv *pdata,
122462306a36Sopenharmony_ci				    int index, u8 addr[ETH_ALEN])
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	u32 temp;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if ((pdata) && (index > 0) && (index < NUM_OF_MAF)) {
122962306a36Sopenharmony_ci		temp = addr[3];
123062306a36Sopenharmony_ci		temp = addr[2] | (temp << 8);
123162306a36Sopenharmony_ci		temp = addr[1] | (temp << 8);
123262306a36Sopenharmony_ci		temp = addr[0] | (temp << 8);
123362306a36Sopenharmony_ci		pdata->pfilter_table[index][1] = temp;
123462306a36Sopenharmony_ci		temp = addr[5];
123562306a36Sopenharmony_ci		temp = addr[4] | (temp << 8);
123662306a36Sopenharmony_ci		temp |= MAF_HI_VALID_ | MAF_HI_TYPE_DST_;
123762306a36Sopenharmony_ci		pdata->pfilter_table[index][0] = temp;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci/* returns hash bit number for given MAC address */
124262306a36Sopenharmony_cistatic inline u32 lan78xx_hash(char addr[ETH_ALEN])
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff;
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic void lan78xx_deferred_multicast_write(struct work_struct *param)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct lan78xx_priv *pdata =
125062306a36Sopenharmony_ci			container_of(param, struct lan78xx_priv, set_multicast);
125162306a36Sopenharmony_ci	struct lan78xx_net *dev = pdata->dev;
125262306a36Sopenharmony_ci	int i;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n",
125562306a36Sopenharmony_ci		  pdata->rfe_ctl);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, DP_SEL_VHF_VLAN_LEN,
125862306a36Sopenharmony_ci			       DP_SEL_VHF_HASH_LEN, pdata->mchash_table);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	for (i = 1; i < NUM_OF_MAF; i++) {
126162306a36Sopenharmony_ci		lan78xx_write_reg(dev, MAF_HI(i), 0);
126262306a36Sopenharmony_ci		lan78xx_write_reg(dev, MAF_LO(i),
126362306a36Sopenharmony_ci				  pdata->pfilter_table[i][1]);
126462306a36Sopenharmony_ci		lan78xx_write_reg(dev, MAF_HI(i),
126562306a36Sopenharmony_ci				  pdata->pfilter_table[i][0]);
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic void lan78xx_set_multicast(struct net_device *netdev)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
127462306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
127562306a36Sopenharmony_ci	unsigned long flags;
127662306a36Sopenharmony_ci	int i;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	pdata->rfe_ctl &= ~(RFE_CTL_UCAST_EN_ | RFE_CTL_MCAST_EN_ |
128162306a36Sopenharmony_ci			    RFE_CTL_DA_PERFECT_ | RFE_CTL_MCAST_HASH_);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++)
128462306a36Sopenharmony_ci		pdata->mchash_table[i] = 0;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/* pfilter_table[0] has own HW address */
128762306a36Sopenharmony_ci	for (i = 1; i < NUM_OF_MAF; i++) {
128862306a36Sopenharmony_ci		pdata->pfilter_table[i][0] = 0;
128962306a36Sopenharmony_ci		pdata->pfilter_table[i][1] = 0;
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	pdata->rfe_ctl |= RFE_CTL_BCAST_EN_;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	if (dev->net->flags & IFF_PROMISC) {
129562306a36Sopenharmony_ci		netif_dbg(dev, drv, dev->net, "promiscuous mode enabled");
129662306a36Sopenharmony_ci		pdata->rfe_ctl |= RFE_CTL_MCAST_EN_ | RFE_CTL_UCAST_EN_;
129762306a36Sopenharmony_ci	} else {
129862306a36Sopenharmony_ci		if (dev->net->flags & IFF_ALLMULTI) {
129962306a36Sopenharmony_ci			netif_dbg(dev, drv, dev->net,
130062306a36Sopenharmony_ci				  "receive all multicast enabled");
130162306a36Sopenharmony_ci			pdata->rfe_ctl |= RFE_CTL_MCAST_EN_;
130262306a36Sopenharmony_ci		}
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (netdev_mc_count(dev->net)) {
130662306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
130762306a36Sopenharmony_ci		int i;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci		netif_dbg(dev, drv, dev->net, "receive multicast hash filter");
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci		pdata->rfe_ctl |= RFE_CTL_DA_PERFECT_;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		i = 1;
131462306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, netdev) {
131562306a36Sopenharmony_ci			/* set first 32 into Perfect Filter */
131662306a36Sopenharmony_ci			if (i < 33) {
131762306a36Sopenharmony_ci				lan78xx_set_addr_filter(pdata, i, ha->addr);
131862306a36Sopenharmony_ci			} else {
131962306a36Sopenharmony_ci				u32 bitnum = lan78xx_hash(ha->addr);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci				pdata->mchash_table[bitnum / 32] |=
132262306a36Sopenharmony_ci							(1 << (bitnum % 32));
132362306a36Sopenharmony_ci				pdata->rfe_ctl |= RFE_CTL_MCAST_HASH_;
132462306a36Sopenharmony_ci			}
132562306a36Sopenharmony_ci			i++;
132662306a36Sopenharmony_ci		}
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	/* defer register writes to a sleepable context */
133262306a36Sopenharmony_ci	schedule_work(&pdata->set_multicast);
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex,
133662306a36Sopenharmony_ci				      u16 lcladv, u16 rmtadv)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	u32 flow = 0, fct_flow = 0;
133962306a36Sopenharmony_ci	u8 cap;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if (dev->fc_autoneg)
134262306a36Sopenharmony_ci		cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
134362306a36Sopenharmony_ci	else
134462306a36Sopenharmony_ci		cap = dev->fc_request_control;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	if (cap & FLOW_CTRL_TX)
134762306a36Sopenharmony_ci		flow |= (FLOW_CR_TX_FCEN_ | 0xFFFF);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if (cap & FLOW_CTRL_RX)
135062306a36Sopenharmony_ci		flow |= FLOW_CR_RX_FCEN_;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (dev->udev->speed == USB_SPEED_SUPER)
135362306a36Sopenharmony_ci		fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_SS, FLOW_OFF_SS);
135462306a36Sopenharmony_ci	else if (dev->udev->speed == USB_SPEED_HIGH)
135562306a36Sopenharmony_ci		fct_flow = FLOW_CTRL_THRESHOLD(FLOW_ON_HS, FLOW_OFF_HS);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s",
135862306a36Sopenharmony_ci		  (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
135962306a36Sopenharmony_ci		  (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	lan78xx_write_reg(dev, FCT_FLOW, fct_flow);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	/* threshold value should be set before enabling flow */
136462306a36Sopenharmony_ci	lan78xx_write_reg(dev, FLOW, flow);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	return 0;
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_cistatic void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_cistatic int lan78xx_mac_reset(struct lan78xx_net *dev)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	unsigned long start_time = jiffies;
137462306a36Sopenharmony_ci	u32 val;
137562306a36Sopenharmony_ci	int ret;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	mutex_lock(&dev->phy_mutex);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	/* Resetting the device while there is activity on the MDIO
138062306a36Sopenharmony_ci	 * bus can result in the MAC interface locking up and not
138162306a36Sopenharmony_ci	 * completing register access transactions.
138262306a36Sopenharmony_ci	 */
138362306a36Sopenharmony_ci	ret = lan78xx_phy_wait_not_busy(dev);
138462306a36Sopenharmony_ci	if (ret < 0)
138562306a36Sopenharmony_ci		goto done;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, MAC_CR, &val);
138862306a36Sopenharmony_ci	if (ret < 0)
138962306a36Sopenharmony_ci		goto done;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	val |= MAC_CR_RST_;
139262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, MAC_CR, val);
139362306a36Sopenharmony_ci	if (ret < 0)
139462306a36Sopenharmony_ci		goto done;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	/* Wait for the reset to complete before allowing any further
139762306a36Sopenharmony_ci	 * MAC register accesses otherwise the MAC may lock up.
139862306a36Sopenharmony_ci	 */
139962306a36Sopenharmony_ci	do {
140062306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, MAC_CR, &val);
140162306a36Sopenharmony_ci		if (ret < 0)
140262306a36Sopenharmony_ci			goto done;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		if (!(val & MAC_CR_RST_)) {
140562306a36Sopenharmony_ci			ret = 0;
140662306a36Sopenharmony_ci			goto done;
140762306a36Sopenharmony_ci		}
140862306a36Sopenharmony_ci	} while (!time_after(jiffies, start_time + HZ));
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	ret = -ETIMEDOUT;
141162306a36Sopenharmony_cidone:
141262306a36Sopenharmony_ci	mutex_unlock(&dev->phy_mutex);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	return ret;
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_cistatic int lan78xx_link_reset(struct lan78xx_net *dev)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	struct phy_device *phydev = dev->net->phydev;
142062306a36Sopenharmony_ci	struct ethtool_link_ksettings ecmd;
142162306a36Sopenharmony_ci	int ladv, radv, ret, link;
142262306a36Sopenharmony_ci	u32 buf;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/* clear LAN78xx interrupt status */
142562306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_);
142662306a36Sopenharmony_ci	if (unlikely(ret < 0))
142762306a36Sopenharmony_ci		return ret;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	mutex_lock(&phydev->lock);
143062306a36Sopenharmony_ci	phy_read_status(phydev);
143162306a36Sopenharmony_ci	link = phydev->link;
143262306a36Sopenharmony_ci	mutex_unlock(&phydev->lock);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (!link && dev->link_on) {
143562306a36Sopenharmony_ci		dev->link_on = false;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		/* reset MAC */
143862306a36Sopenharmony_ci		ret = lan78xx_mac_reset(dev);
143962306a36Sopenharmony_ci		if (ret < 0)
144062306a36Sopenharmony_ci			return ret;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci		del_timer(&dev->stat_monitor);
144362306a36Sopenharmony_ci	} else if (link && !dev->link_on) {
144462306a36Sopenharmony_ci		dev->link_on = true;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci		phy_ethtool_ksettings_get(phydev, &ecmd);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci		if (dev->udev->speed == USB_SPEED_SUPER) {
144962306a36Sopenharmony_ci			if (ecmd.base.speed == 1000) {
145062306a36Sopenharmony_ci				/* disable U2 */
145162306a36Sopenharmony_ci				ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
145262306a36Sopenharmony_ci				if (ret < 0)
145362306a36Sopenharmony_ci					return ret;
145462306a36Sopenharmony_ci				buf &= ~USB_CFG1_DEV_U2_INIT_EN_;
145562306a36Sopenharmony_ci				ret = lan78xx_write_reg(dev, USB_CFG1, buf);
145662306a36Sopenharmony_ci				if (ret < 0)
145762306a36Sopenharmony_ci					return ret;
145862306a36Sopenharmony_ci				/* enable U1 */
145962306a36Sopenharmony_ci				ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
146062306a36Sopenharmony_ci				if (ret < 0)
146162306a36Sopenharmony_ci					return ret;
146262306a36Sopenharmony_ci				buf |= USB_CFG1_DEV_U1_INIT_EN_;
146362306a36Sopenharmony_ci				ret = lan78xx_write_reg(dev, USB_CFG1, buf);
146462306a36Sopenharmony_ci				if (ret < 0)
146562306a36Sopenharmony_ci					return ret;
146662306a36Sopenharmony_ci			} else {
146762306a36Sopenharmony_ci				/* enable U1 & U2 */
146862306a36Sopenharmony_ci				ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
146962306a36Sopenharmony_ci				if (ret < 0)
147062306a36Sopenharmony_ci					return ret;
147162306a36Sopenharmony_ci				buf |= USB_CFG1_DEV_U2_INIT_EN_;
147262306a36Sopenharmony_ci				buf |= USB_CFG1_DEV_U1_INIT_EN_;
147362306a36Sopenharmony_ci				ret = lan78xx_write_reg(dev, USB_CFG1, buf);
147462306a36Sopenharmony_ci				if (ret < 0)
147562306a36Sopenharmony_ci					return ret;
147662306a36Sopenharmony_ci			}
147762306a36Sopenharmony_ci		}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci		ladv = phy_read(phydev, MII_ADVERTISE);
148062306a36Sopenharmony_ci		if (ladv < 0)
148162306a36Sopenharmony_ci			return ladv;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci		radv = phy_read(phydev, MII_LPA);
148462306a36Sopenharmony_ci		if (radv < 0)
148562306a36Sopenharmony_ci			return radv;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		netif_dbg(dev, link, dev->net,
148862306a36Sopenharmony_ci			  "speed: %u duplex: %d anadv: 0x%04x anlpa: 0x%04x",
148962306a36Sopenharmony_ci			  ecmd.base.speed, ecmd.base.duplex, ladv, radv);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci		ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv,
149262306a36Sopenharmony_ci						 radv);
149362306a36Sopenharmony_ci		if (ret < 0)
149462306a36Sopenharmony_ci			return ret;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci		if (!timer_pending(&dev->stat_monitor)) {
149762306a36Sopenharmony_ci			dev->delta = 1;
149862306a36Sopenharmony_ci			mod_timer(&dev->stat_monitor,
149962306a36Sopenharmony_ci				  jiffies + STAT_UPDATE_TIMER);
150062306a36Sopenharmony_ci		}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci		lan78xx_rx_urb_submit_all(dev);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci		local_bh_disable();
150562306a36Sopenharmony_ci		napi_schedule(&dev->napi);
150662306a36Sopenharmony_ci		local_bh_enable();
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return 0;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci/* some work can't be done in tasklets, so we use keventd
151362306a36Sopenharmony_ci *
151462306a36Sopenharmony_ci * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails,
151562306a36Sopenharmony_ci * but tasklet_schedule() doesn't.	hope the failure is rare.
151662306a36Sopenharmony_ci */
151762306a36Sopenharmony_cistatic void lan78xx_defer_kevent(struct lan78xx_net *dev, int work)
151862306a36Sopenharmony_ci{
151962306a36Sopenharmony_ci	set_bit(work, &dev->flags);
152062306a36Sopenharmony_ci	if (!schedule_delayed_work(&dev->wq, 0))
152162306a36Sopenharmony_ci		netdev_err(dev->net, "kevent %d may have been dropped\n", work);
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic void lan78xx_status(struct lan78xx_net *dev, struct urb *urb)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	u32 intdata;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	if (urb->actual_length != 4) {
152962306a36Sopenharmony_ci		netdev_warn(dev->net,
153062306a36Sopenharmony_ci			    "unexpected urb length %d", urb->actual_length);
153162306a36Sopenharmony_ci		return;
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	intdata = get_unaligned_le32(urb->transfer_buffer);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (intdata & INT_ENP_PHY_INT) {
153762306a36Sopenharmony_ci		netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata);
153862306a36Sopenharmony_ci		lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci		if (dev->domain_data.phyirq > 0)
154162306a36Sopenharmony_ci			generic_handle_irq_safe(dev->domain_data.phyirq);
154262306a36Sopenharmony_ci	} else {
154362306a36Sopenharmony_ci		netdev_warn(dev->net,
154462306a36Sopenharmony_ci			    "unexpected interrupt: 0x%08x\n", intdata);
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic int lan78xx_ethtool_get_eeprom_len(struct net_device *netdev)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	return MAX_EEPROM_SIZE;
155162306a36Sopenharmony_ci}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic int lan78xx_ethtool_get_eeprom(struct net_device *netdev,
155462306a36Sopenharmony_ci				      struct ethtool_eeprom *ee, u8 *data)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
155762306a36Sopenharmony_ci	int ret;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
156062306a36Sopenharmony_ci	if (ret)
156162306a36Sopenharmony_ci		return ret;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	ee->magic = LAN78XX_EEPROM_MAGIC;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	ret = lan78xx_read_raw_eeprom(dev, ee->offset, ee->len, data);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	return ret;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_cistatic int lan78xx_ethtool_set_eeprom(struct net_device *netdev,
157362306a36Sopenharmony_ci				      struct ethtool_eeprom *ee, u8 *data)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
157662306a36Sopenharmony_ci	int ret;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
157962306a36Sopenharmony_ci	if (ret)
158062306a36Sopenharmony_ci		return ret;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	/* Invalid EEPROM_INDICATOR at offset zero will result in a failure
158362306a36Sopenharmony_ci	 * to load data from EEPROM
158462306a36Sopenharmony_ci	 */
158562306a36Sopenharmony_ci	if (ee->magic == LAN78XX_EEPROM_MAGIC)
158662306a36Sopenharmony_ci		ret = lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data);
158762306a36Sopenharmony_ci	else if ((ee->magic == LAN78XX_OTP_MAGIC) &&
158862306a36Sopenharmony_ci		 (ee->offset == 0) &&
158962306a36Sopenharmony_ci		 (ee->len == 512) &&
159062306a36Sopenharmony_ci		 (data[0] == OTP_INDICATOR_1))
159162306a36Sopenharmony_ci		ret = lan78xx_write_raw_otp(dev, ee->offset, ee->len, data);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	return ret;
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_cistatic void lan78xx_get_strings(struct net_device *netdev, u32 stringset,
159962306a36Sopenharmony_ci				u8 *data)
160062306a36Sopenharmony_ci{
160162306a36Sopenharmony_ci	if (stringset == ETH_SS_STATS)
160262306a36Sopenharmony_ci		memcpy(data, lan78xx_gstrings, sizeof(lan78xx_gstrings));
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int lan78xx_get_sset_count(struct net_device *netdev, int sset)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	if (sset == ETH_SS_STATS)
160862306a36Sopenharmony_ci		return ARRAY_SIZE(lan78xx_gstrings);
160962306a36Sopenharmony_ci	else
161062306a36Sopenharmony_ci		return -EOPNOTSUPP;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic void lan78xx_get_stats(struct net_device *netdev,
161462306a36Sopenharmony_ci			      struct ethtool_stats *stats, u64 *data)
161562306a36Sopenharmony_ci{
161662306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	lan78xx_update_stats(dev);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	mutex_lock(&dev->stats.access_lock);
162162306a36Sopenharmony_ci	memcpy(data, &dev->stats.curr_stat, sizeof(dev->stats.curr_stat));
162262306a36Sopenharmony_ci	mutex_unlock(&dev->stats.access_lock);
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic void lan78xx_get_wol(struct net_device *netdev,
162662306a36Sopenharmony_ci			    struct ethtool_wolinfo *wol)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
162962306a36Sopenharmony_ci	int ret;
163062306a36Sopenharmony_ci	u32 buf;
163162306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (usb_autopm_get_interface(dev->intf) < 0)
163462306a36Sopenharmony_ci		return;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
163762306a36Sopenharmony_ci	if (unlikely(ret < 0)) {
163862306a36Sopenharmony_ci		wol->supported = 0;
163962306a36Sopenharmony_ci		wol->wolopts = 0;
164062306a36Sopenharmony_ci	} else {
164162306a36Sopenharmony_ci		if (buf & USB_CFG_RMT_WKP_) {
164262306a36Sopenharmony_ci			wol->supported = WAKE_ALL;
164362306a36Sopenharmony_ci			wol->wolopts = pdata->wol;
164462306a36Sopenharmony_ci		} else {
164562306a36Sopenharmony_ci			wol->supported = 0;
164662306a36Sopenharmony_ci			wol->wolopts = 0;
164762306a36Sopenharmony_ci		}
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic int lan78xx_set_wol(struct net_device *netdev,
165462306a36Sopenharmony_ci			   struct ethtool_wolinfo *wol)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
165762306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
165862306a36Sopenharmony_ci	int ret;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
166162306a36Sopenharmony_ci	if (ret < 0)
166262306a36Sopenharmony_ci		return ret;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	if (wol->wolopts & ~WAKE_ALL)
166562306a36Sopenharmony_ci		return -EINVAL;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	pdata->wol = wol->wolopts;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	phy_ethtool_set_wol(netdev->phydev, wol);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	return ret;
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistatic int lan78xx_get_eee(struct net_device *net, struct ethtool_eee *edata)
167962306a36Sopenharmony_ci{
168062306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
168162306a36Sopenharmony_ci	struct phy_device *phydev = net->phydev;
168262306a36Sopenharmony_ci	int ret;
168362306a36Sopenharmony_ci	u32 buf;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
168662306a36Sopenharmony_ci	if (ret < 0)
168762306a36Sopenharmony_ci		return ret;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	ret = phy_ethtool_get_eee(phydev, edata);
169062306a36Sopenharmony_ci	if (ret < 0)
169162306a36Sopenharmony_ci		goto exit;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, MAC_CR, &buf);
169462306a36Sopenharmony_ci	if (buf & MAC_CR_EEE_EN_) {
169562306a36Sopenharmony_ci		edata->eee_enabled = true;
169662306a36Sopenharmony_ci		edata->eee_active = !!(edata->advertised &
169762306a36Sopenharmony_ci				       edata->lp_advertised);
169862306a36Sopenharmony_ci		edata->tx_lpi_enabled = true;
169962306a36Sopenharmony_ci		/* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */
170062306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf);
170162306a36Sopenharmony_ci		edata->tx_lpi_timer = buf;
170262306a36Sopenharmony_ci	} else {
170362306a36Sopenharmony_ci		edata->eee_enabled = false;
170462306a36Sopenharmony_ci		edata->eee_active = false;
170562306a36Sopenharmony_ci		edata->tx_lpi_enabled = false;
170662306a36Sopenharmony_ci		edata->tx_lpi_timer = 0;
170762306a36Sopenharmony_ci	}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	ret = 0;
171062306a36Sopenharmony_ciexit:
171162306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	return ret;
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_cistatic int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata)
171762306a36Sopenharmony_ci{
171862306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
171962306a36Sopenharmony_ci	int ret;
172062306a36Sopenharmony_ci	u32 buf;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
172362306a36Sopenharmony_ci	if (ret < 0)
172462306a36Sopenharmony_ci		return ret;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	if (edata->eee_enabled) {
172762306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, MAC_CR, &buf);
172862306a36Sopenharmony_ci		buf |= MAC_CR_EEE_EN_;
172962306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, MAC_CR, buf);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci		phy_ethtool_set_eee(net->phydev, edata);
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		buf = (u32)edata->tx_lpi_timer;
173462306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf);
173562306a36Sopenharmony_ci	} else {
173662306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, MAC_CR, &buf);
173762306a36Sopenharmony_ci		buf &= ~MAC_CR_EEE_EN_;
173862306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, MAC_CR, buf);
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	return 0;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic u32 lan78xx_get_link(struct net_device *net)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	u32 link;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	mutex_lock(&net->phydev->lock);
175162306a36Sopenharmony_ci	phy_read_status(net->phydev);
175262306a36Sopenharmony_ci	link = net->phydev->link;
175362306a36Sopenharmony_ci	mutex_unlock(&net->phydev->lock);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	return link;
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic void lan78xx_get_drvinfo(struct net_device *net,
175962306a36Sopenharmony_ci				struct ethtool_drvinfo *info)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
176462306a36Sopenharmony_ci	usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_cistatic u32 lan78xx_get_msglevel(struct net_device *net)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	return dev->msg_enable;
177262306a36Sopenharmony_ci}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_cistatic void lan78xx_set_msglevel(struct net_device *net, u32 level)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	dev->msg_enable = level;
177962306a36Sopenharmony_ci}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_cistatic int lan78xx_get_link_ksettings(struct net_device *net,
178262306a36Sopenharmony_ci				      struct ethtool_link_ksettings *cmd)
178362306a36Sopenharmony_ci{
178462306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
178562306a36Sopenharmony_ci	struct phy_device *phydev = net->phydev;
178662306a36Sopenharmony_ci	int ret;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
178962306a36Sopenharmony_ci	if (ret < 0)
179062306a36Sopenharmony_ci		return ret;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	phy_ethtool_ksettings_get(phydev, cmd);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	return ret;
179762306a36Sopenharmony_ci}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_cistatic int lan78xx_set_link_ksettings(struct net_device *net,
180062306a36Sopenharmony_ci				      const struct ethtool_link_ksettings *cmd)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
180362306a36Sopenharmony_ci	struct phy_device *phydev = net->phydev;
180462306a36Sopenharmony_ci	int ret = 0;
180562306a36Sopenharmony_ci	int temp;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
180862306a36Sopenharmony_ci	if (ret < 0)
180962306a36Sopenharmony_ci		return ret;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	/* change speed & duplex */
181262306a36Sopenharmony_ci	ret = phy_ethtool_ksettings_set(phydev, cmd);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	if (!cmd->base.autoneg) {
181562306a36Sopenharmony_ci		/* force link down */
181662306a36Sopenharmony_ci		temp = phy_read(phydev, MII_BMCR);
181762306a36Sopenharmony_ci		phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK);
181862306a36Sopenharmony_ci		mdelay(1);
181962306a36Sopenharmony_ci		phy_write(phydev, MII_BMCR, temp);
182062306a36Sopenharmony_ci	}
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	return ret;
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_cistatic void lan78xx_get_pause(struct net_device *net,
182862306a36Sopenharmony_ci			      struct ethtool_pauseparam *pause)
182962306a36Sopenharmony_ci{
183062306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
183162306a36Sopenharmony_ci	struct phy_device *phydev = net->phydev;
183262306a36Sopenharmony_ci	struct ethtool_link_ksettings ecmd;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	phy_ethtool_ksettings_get(phydev, &ecmd);
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	pause->autoneg = dev->fc_autoneg;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	if (dev->fc_request_control & FLOW_CTRL_TX)
183962306a36Sopenharmony_ci		pause->tx_pause = 1;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (dev->fc_request_control & FLOW_CTRL_RX)
184262306a36Sopenharmony_ci		pause->rx_pause = 1;
184362306a36Sopenharmony_ci}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_cistatic int lan78xx_set_pause(struct net_device *net,
184662306a36Sopenharmony_ci			     struct ethtool_pauseparam *pause)
184762306a36Sopenharmony_ci{
184862306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
184962306a36Sopenharmony_ci	struct phy_device *phydev = net->phydev;
185062306a36Sopenharmony_ci	struct ethtool_link_ksettings ecmd;
185162306a36Sopenharmony_ci	int ret;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	phy_ethtool_ksettings_get(phydev, &ecmd);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (pause->autoneg && !ecmd.base.autoneg) {
185662306a36Sopenharmony_ci		ret = -EINVAL;
185762306a36Sopenharmony_ci		goto exit;
185862306a36Sopenharmony_ci	}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	dev->fc_request_control = 0;
186162306a36Sopenharmony_ci	if (pause->rx_pause)
186262306a36Sopenharmony_ci		dev->fc_request_control |= FLOW_CTRL_RX;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	if (pause->tx_pause)
186562306a36Sopenharmony_ci		dev->fc_request_control |= FLOW_CTRL_TX;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (ecmd.base.autoneg) {
186862306a36Sopenharmony_ci		__ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
186962306a36Sopenharmony_ci		u32 mii_adv;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci		linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
187262306a36Sopenharmony_ci				   ecmd.link_modes.advertising);
187362306a36Sopenharmony_ci		linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
187462306a36Sopenharmony_ci				   ecmd.link_modes.advertising);
187562306a36Sopenharmony_ci		mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
187662306a36Sopenharmony_ci		mii_adv_to_linkmode_adv_t(fc, mii_adv);
187762306a36Sopenharmony_ci		linkmode_or(ecmd.link_modes.advertising, fc,
187862306a36Sopenharmony_ci			    ecmd.link_modes.advertising);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci		phy_ethtool_ksettings_set(phydev, &ecmd);
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	dev->fc_autoneg = pause->autoneg;
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	ret = 0;
188662306a36Sopenharmony_ciexit:
188762306a36Sopenharmony_ci	return ret;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_cistatic int lan78xx_get_regs_len(struct net_device *netdev)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	if (!netdev->phydev)
189362306a36Sopenharmony_ci		return (sizeof(lan78xx_regs));
189462306a36Sopenharmony_ci	else
189562306a36Sopenharmony_ci		return (sizeof(lan78xx_regs) + PHY_REG_SIZE);
189662306a36Sopenharmony_ci}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_cistatic void
189962306a36Sopenharmony_cilan78xx_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
190062306a36Sopenharmony_ci		 void *buf)
190162306a36Sopenharmony_ci{
190262306a36Sopenharmony_ci	u32 *data = buf;
190362306a36Sopenharmony_ci	int i, j;
190462306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	/* Read Device/MAC registers */
190762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lan78xx_regs); i++)
190862306a36Sopenharmony_ci		lan78xx_read_reg(dev, lan78xx_regs[i], &data[i]);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	if (!netdev->phydev)
191162306a36Sopenharmony_ci		return;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	/* Read PHY registers */
191462306a36Sopenharmony_ci	for (j = 0; j < 32; i++, j++)
191562306a36Sopenharmony_ci		data[i] = phy_read(netdev->phydev, j);
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_cistatic const struct ethtool_ops lan78xx_ethtool_ops = {
191962306a36Sopenharmony_ci	.get_link	= lan78xx_get_link,
192062306a36Sopenharmony_ci	.nway_reset	= phy_ethtool_nway_reset,
192162306a36Sopenharmony_ci	.get_drvinfo	= lan78xx_get_drvinfo,
192262306a36Sopenharmony_ci	.get_msglevel	= lan78xx_get_msglevel,
192362306a36Sopenharmony_ci	.set_msglevel	= lan78xx_set_msglevel,
192462306a36Sopenharmony_ci	.get_eeprom_len = lan78xx_ethtool_get_eeprom_len,
192562306a36Sopenharmony_ci	.get_eeprom	= lan78xx_ethtool_get_eeprom,
192662306a36Sopenharmony_ci	.set_eeprom	= lan78xx_ethtool_set_eeprom,
192762306a36Sopenharmony_ci	.get_ethtool_stats = lan78xx_get_stats,
192862306a36Sopenharmony_ci	.get_sset_count = lan78xx_get_sset_count,
192962306a36Sopenharmony_ci	.get_strings	= lan78xx_get_strings,
193062306a36Sopenharmony_ci	.get_wol	= lan78xx_get_wol,
193162306a36Sopenharmony_ci	.set_wol	= lan78xx_set_wol,
193262306a36Sopenharmony_ci	.get_ts_info	= ethtool_op_get_ts_info,
193362306a36Sopenharmony_ci	.get_eee	= lan78xx_get_eee,
193462306a36Sopenharmony_ci	.set_eee	= lan78xx_set_eee,
193562306a36Sopenharmony_ci	.get_pauseparam	= lan78xx_get_pause,
193662306a36Sopenharmony_ci	.set_pauseparam	= lan78xx_set_pause,
193762306a36Sopenharmony_ci	.get_link_ksettings = lan78xx_get_link_ksettings,
193862306a36Sopenharmony_ci	.set_link_ksettings = lan78xx_set_link_ksettings,
193962306a36Sopenharmony_ci	.get_regs_len	= lan78xx_get_regs_len,
194062306a36Sopenharmony_ci	.get_regs	= lan78xx_get_regs,
194162306a36Sopenharmony_ci};
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_cistatic void lan78xx_init_mac_address(struct lan78xx_net *dev)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	u32 addr_lo, addr_hi;
194662306a36Sopenharmony_ci	u8 addr[6];
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	lan78xx_read_reg(dev, RX_ADDRL, &addr_lo);
194962306a36Sopenharmony_ci	lan78xx_read_reg(dev, RX_ADDRH, &addr_hi);
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	addr[0] = addr_lo & 0xFF;
195262306a36Sopenharmony_ci	addr[1] = (addr_lo >> 8) & 0xFF;
195362306a36Sopenharmony_ci	addr[2] = (addr_lo >> 16) & 0xFF;
195462306a36Sopenharmony_ci	addr[3] = (addr_lo >> 24) & 0xFF;
195562306a36Sopenharmony_ci	addr[4] = addr_hi & 0xFF;
195662306a36Sopenharmony_ci	addr[5] = (addr_hi >> 8) & 0xFF;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr)) {
195962306a36Sopenharmony_ci		if (!eth_platform_get_mac_address(&dev->udev->dev, addr)) {
196062306a36Sopenharmony_ci			/* valid address present in Device Tree */
196162306a36Sopenharmony_ci			netif_dbg(dev, ifup, dev->net,
196262306a36Sopenharmony_ci				  "MAC address read from Device Tree");
196362306a36Sopenharmony_ci		} else if (((lan78xx_read_eeprom(dev, EEPROM_MAC_OFFSET,
196462306a36Sopenharmony_ci						 ETH_ALEN, addr) == 0) ||
196562306a36Sopenharmony_ci			    (lan78xx_read_otp(dev, EEPROM_MAC_OFFSET,
196662306a36Sopenharmony_ci					      ETH_ALEN, addr) == 0)) &&
196762306a36Sopenharmony_ci			   is_valid_ether_addr(addr)) {
196862306a36Sopenharmony_ci			/* eeprom values are valid so use them */
196962306a36Sopenharmony_ci			netif_dbg(dev, ifup, dev->net,
197062306a36Sopenharmony_ci				  "MAC address read from EEPROM");
197162306a36Sopenharmony_ci		} else {
197262306a36Sopenharmony_ci			/* generate random MAC */
197362306a36Sopenharmony_ci			eth_random_addr(addr);
197462306a36Sopenharmony_ci			netif_dbg(dev, ifup, dev->net,
197562306a36Sopenharmony_ci				  "MAC address set to random addr");
197662306a36Sopenharmony_ci		}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci		addr_lo = addr[0] | (addr[1] << 8) |
197962306a36Sopenharmony_ci			  (addr[2] << 16) | (addr[3] << 24);
198062306a36Sopenharmony_ci		addr_hi = addr[4] | (addr[5] << 8);
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci		lan78xx_write_reg(dev, RX_ADDRL, addr_lo);
198362306a36Sopenharmony_ci		lan78xx_write_reg(dev, RX_ADDRH, addr_hi);
198462306a36Sopenharmony_ci	}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	lan78xx_write_reg(dev, MAF_LO(0), addr_lo);
198762306a36Sopenharmony_ci	lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	eth_hw_addr_set(dev->net, addr);
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci/* MDIO read and write wrappers for phylib */
199362306a36Sopenharmony_cistatic int lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx)
199462306a36Sopenharmony_ci{
199562306a36Sopenharmony_ci	struct lan78xx_net *dev = bus->priv;
199662306a36Sopenharmony_ci	u32 val, addr;
199762306a36Sopenharmony_ci	int ret;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
200062306a36Sopenharmony_ci	if (ret < 0)
200162306a36Sopenharmony_ci		return ret;
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	mutex_lock(&dev->phy_mutex);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* confirm MII not busy */
200662306a36Sopenharmony_ci	ret = lan78xx_phy_wait_not_busy(dev);
200762306a36Sopenharmony_ci	if (ret < 0)
200862306a36Sopenharmony_ci		goto done;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	/* set the address, index & direction (read from PHY) */
201162306a36Sopenharmony_ci	addr = mii_access(phy_id, idx, MII_READ);
201262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, MII_ACC, addr);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	ret = lan78xx_phy_wait_not_busy(dev);
201562306a36Sopenharmony_ci	if (ret < 0)
201662306a36Sopenharmony_ci		goto done;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, MII_DATA, &val);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	ret = (int)(val & 0xFFFF);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_cidone:
202362306a36Sopenharmony_ci	mutex_unlock(&dev->phy_mutex);
202462306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	return ret;
202762306a36Sopenharmony_ci}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_cistatic int lan78xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx,
203062306a36Sopenharmony_ci				 u16 regval)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct lan78xx_net *dev = bus->priv;
203362306a36Sopenharmony_ci	u32 val, addr;
203462306a36Sopenharmony_ci	int ret;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
203762306a36Sopenharmony_ci	if (ret < 0)
203862306a36Sopenharmony_ci		return ret;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	mutex_lock(&dev->phy_mutex);
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	/* confirm MII not busy */
204362306a36Sopenharmony_ci	ret = lan78xx_phy_wait_not_busy(dev);
204462306a36Sopenharmony_ci	if (ret < 0)
204562306a36Sopenharmony_ci		goto done;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	val = (u32)regval;
204862306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, MII_DATA, val);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	/* set the address, index & direction (write to PHY) */
205162306a36Sopenharmony_ci	addr = mii_access(phy_id, idx, MII_WRITE);
205262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, MII_ACC, addr);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	ret = lan78xx_phy_wait_not_busy(dev);
205562306a36Sopenharmony_ci	if (ret < 0)
205662306a36Sopenharmony_ci		goto done;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_cidone:
205962306a36Sopenharmony_ci	mutex_unlock(&dev->phy_mutex);
206062306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
206162306a36Sopenharmony_ci	return 0;
206262306a36Sopenharmony_ci}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_cistatic int lan78xx_mdio_init(struct lan78xx_net *dev)
206562306a36Sopenharmony_ci{
206662306a36Sopenharmony_ci	struct device_node *node;
206762306a36Sopenharmony_ci	int ret;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	dev->mdiobus = mdiobus_alloc();
207062306a36Sopenharmony_ci	if (!dev->mdiobus) {
207162306a36Sopenharmony_ci		netdev_err(dev->net, "can't allocate MDIO bus\n");
207262306a36Sopenharmony_ci		return -ENOMEM;
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	dev->mdiobus->priv = (void *)dev;
207662306a36Sopenharmony_ci	dev->mdiobus->read = lan78xx_mdiobus_read;
207762306a36Sopenharmony_ci	dev->mdiobus->write = lan78xx_mdiobus_write;
207862306a36Sopenharmony_ci	dev->mdiobus->name = "lan78xx-mdiobus";
207962306a36Sopenharmony_ci	dev->mdiobus->parent = &dev->udev->dev;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
208262306a36Sopenharmony_ci		 dev->udev->bus->busnum, dev->udev->devnum);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	switch (dev->chipid) {
208562306a36Sopenharmony_ci	case ID_REV_CHIP_ID_7800_:
208662306a36Sopenharmony_ci	case ID_REV_CHIP_ID_7850_:
208762306a36Sopenharmony_ci		/* set to internal PHY id */
208862306a36Sopenharmony_ci		dev->mdiobus->phy_mask = ~(1 << 1);
208962306a36Sopenharmony_ci		break;
209062306a36Sopenharmony_ci	case ID_REV_CHIP_ID_7801_:
209162306a36Sopenharmony_ci		/* scan thru PHYAD[2..0] */
209262306a36Sopenharmony_ci		dev->mdiobus->phy_mask = ~(0xFF);
209362306a36Sopenharmony_ci		break;
209462306a36Sopenharmony_ci	}
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	node = of_get_child_by_name(dev->udev->dev.of_node, "mdio");
209762306a36Sopenharmony_ci	ret = of_mdiobus_register(dev->mdiobus, node);
209862306a36Sopenharmony_ci	of_node_put(node);
209962306a36Sopenharmony_ci	if (ret) {
210062306a36Sopenharmony_ci		netdev_err(dev->net, "can't register MDIO bus\n");
210162306a36Sopenharmony_ci		goto exit1;
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id);
210562306a36Sopenharmony_ci	return 0;
210662306a36Sopenharmony_ciexit1:
210762306a36Sopenharmony_ci	mdiobus_free(dev->mdiobus);
210862306a36Sopenharmony_ci	return ret;
210962306a36Sopenharmony_ci}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_cistatic void lan78xx_remove_mdio(struct lan78xx_net *dev)
211262306a36Sopenharmony_ci{
211362306a36Sopenharmony_ci	mdiobus_unregister(dev->mdiobus);
211462306a36Sopenharmony_ci	mdiobus_free(dev->mdiobus);
211562306a36Sopenharmony_ci}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_cistatic void lan78xx_link_status_change(struct net_device *net)
211862306a36Sopenharmony_ci{
211962306a36Sopenharmony_ci	struct phy_device *phydev = net->phydev;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	phy_print_status(phydev);
212262306a36Sopenharmony_ci}
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_cistatic int irq_map(struct irq_domain *d, unsigned int irq,
212562306a36Sopenharmony_ci		   irq_hw_number_t hwirq)
212662306a36Sopenharmony_ci{
212762306a36Sopenharmony_ci	struct irq_domain_data *data = d->host_data;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	irq_set_chip_data(irq, data);
213062306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, data->irqchip, data->irq_handler);
213162306a36Sopenharmony_ci	irq_set_noprobe(irq);
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	return 0;
213462306a36Sopenharmony_ci}
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_cistatic void irq_unmap(struct irq_domain *d, unsigned int irq)
213762306a36Sopenharmony_ci{
213862306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, NULL, NULL);
213962306a36Sopenharmony_ci	irq_set_chip_data(irq, NULL);
214062306a36Sopenharmony_ci}
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_cistatic const struct irq_domain_ops chip_domain_ops = {
214362306a36Sopenharmony_ci	.map	= irq_map,
214462306a36Sopenharmony_ci	.unmap	= irq_unmap,
214562306a36Sopenharmony_ci};
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_cistatic void lan78xx_irq_mask(struct irq_data *irqd)
214862306a36Sopenharmony_ci{
214962306a36Sopenharmony_ci	struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	data->irqenable &= ~BIT(irqd_to_hwirq(irqd));
215262306a36Sopenharmony_ci}
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_cistatic void lan78xx_irq_unmask(struct irq_data *irqd)
215562306a36Sopenharmony_ci{
215662306a36Sopenharmony_ci	struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	data->irqenable |= BIT(irqd_to_hwirq(irqd));
215962306a36Sopenharmony_ci}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_cistatic void lan78xx_irq_bus_lock(struct irq_data *irqd)
216262306a36Sopenharmony_ci{
216362306a36Sopenharmony_ci	struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd);
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	mutex_lock(&data->irq_lock);
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic void lan78xx_irq_bus_sync_unlock(struct irq_data *irqd)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	struct irq_domain_data *data = irq_data_get_irq_chip_data(irqd);
217162306a36Sopenharmony_ci	struct lan78xx_net *dev =
217262306a36Sopenharmony_ci			container_of(data, struct lan78xx_net, domain_data);
217362306a36Sopenharmony_ci	u32 buf;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	/* call register access here because irq_bus_lock & irq_bus_sync_unlock
217662306a36Sopenharmony_ci	 * are only two callbacks executed in non-atomic contex.
217762306a36Sopenharmony_ci	 */
217862306a36Sopenharmony_ci	lan78xx_read_reg(dev, INT_EP_CTL, &buf);
217962306a36Sopenharmony_ci	if (buf != data->irqenable)
218062306a36Sopenharmony_ci		lan78xx_write_reg(dev, INT_EP_CTL, data->irqenable);
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	mutex_unlock(&data->irq_lock);
218362306a36Sopenharmony_ci}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_cistatic struct irq_chip lan78xx_irqchip = {
218662306a36Sopenharmony_ci	.name			= "lan78xx-irqs",
218762306a36Sopenharmony_ci	.irq_mask		= lan78xx_irq_mask,
218862306a36Sopenharmony_ci	.irq_unmask		= lan78xx_irq_unmask,
218962306a36Sopenharmony_ci	.irq_bus_lock		= lan78xx_irq_bus_lock,
219062306a36Sopenharmony_ci	.irq_bus_sync_unlock	= lan78xx_irq_bus_sync_unlock,
219162306a36Sopenharmony_ci};
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_cistatic int lan78xx_setup_irq_domain(struct lan78xx_net *dev)
219462306a36Sopenharmony_ci{
219562306a36Sopenharmony_ci	struct device_node *of_node;
219662306a36Sopenharmony_ci	struct irq_domain *irqdomain;
219762306a36Sopenharmony_ci	unsigned int irqmap = 0;
219862306a36Sopenharmony_ci	u32 buf;
219962306a36Sopenharmony_ci	int ret = 0;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	of_node = dev->udev->dev.parent->of_node;
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	mutex_init(&dev->domain_data.irq_lock);
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	lan78xx_read_reg(dev, INT_EP_CTL, &buf);
220662306a36Sopenharmony_ci	dev->domain_data.irqenable = buf;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	dev->domain_data.irqchip = &lan78xx_irqchip;
220962306a36Sopenharmony_ci	dev->domain_data.irq_handler = handle_simple_irq;
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	irqdomain = irq_domain_add_simple(of_node, MAX_INT_EP, 0,
221262306a36Sopenharmony_ci					  &chip_domain_ops, &dev->domain_data);
221362306a36Sopenharmony_ci	if (irqdomain) {
221462306a36Sopenharmony_ci		/* create mapping for PHY interrupt */
221562306a36Sopenharmony_ci		irqmap = irq_create_mapping(irqdomain, INT_EP_PHY);
221662306a36Sopenharmony_ci		if (!irqmap) {
221762306a36Sopenharmony_ci			irq_domain_remove(irqdomain);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci			irqdomain = NULL;
222062306a36Sopenharmony_ci			ret = -EINVAL;
222162306a36Sopenharmony_ci		}
222262306a36Sopenharmony_ci	} else {
222362306a36Sopenharmony_ci		ret = -EINVAL;
222462306a36Sopenharmony_ci	}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	dev->domain_data.irqdomain = irqdomain;
222762306a36Sopenharmony_ci	dev->domain_data.phyirq = irqmap;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	return ret;
223062306a36Sopenharmony_ci}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_cistatic void lan78xx_remove_irq_domain(struct lan78xx_net *dev)
223362306a36Sopenharmony_ci{
223462306a36Sopenharmony_ci	if (dev->domain_data.phyirq > 0) {
223562306a36Sopenharmony_ci		irq_dispose_mapping(dev->domain_data.phyirq);
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci		if (dev->domain_data.irqdomain)
223862306a36Sopenharmony_ci			irq_domain_remove(dev->domain_data.irqdomain);
223962306a36Sopenharmony_ci	}
224062306a36Sopenharmony_ci	dev->domain_data.phyirq = 0;
224162306a36Sopenharmony_ci	dev->domain_data.irqdomain = NULL;
224262306a36Sopenharmony_ci}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_cistatic int lan8835_fixup(struct phy_device *phydev)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	int buf;
224762306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(phydev->attached_dev);
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	/* LED2/PME_N/IRQ_N/RGMII_ID pin to IRQ_N mode */
225062306a36Sopenharmony_ci	buf = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x8010);
225162306a36Sopenharmony_ci	buf &= ~0x1800;
225262306a36Sopenharmony_ci	buf |= 0x0800;
225362306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8010, buf);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	/* RGMII MAC TXC Delay Enable */
225662306a36Sopenharmony_ci	lan78xx_write_reg(dev, MAC_RGMII_ID,
225762306a36Sopenharmony_ci			  MAC_RGMII_ID_TXC_DELAY_EN_);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	/* RGMII TX DLL Tune Adjust */
226062306a36Sopenharmony_ci	lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	dev->interface = PHY_INTERFACE_MODE_RGMII_TXID;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	return 1;
226562306a36Sopenharmony_ci}
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_cistatic int ksz9031rnx_fixup(struct phy_device *phydev)
226862306a36Sopenharmony_ci{
226962306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(phydev->attached_dev);
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	/* Micrel9301RNX PHY configuration */
227262306a36Sopenharmony_ci	/* RGMII Control Signal Pad Skew */
227362306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_WIS, 4, 0x0077);
227462306a36Sopenharmony_ci	/* RGMII RX Data Pad Skew */
227562306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_WIS, 5, 0x7777);
227662306a36Sopenharmony_ci	/* RGMII RX Clock Pad Skew */
227762306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_WIS, 8, 0x1FF);
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	dev->interface = PHY_INTERFACE_MODE_RGMII_RXID;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	return 1;
228262306a36Sopenharmony_ci}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_cistatic struct phy_device *lan7801_phy_init(struct lan78xx_net *dev)
228562306a36Sopenharmony_ci{
228662306a36Sopenharmony_ci	u32 buf;
228762306a36Sopenharmony_ci	int ret;
228862306a36Sopenharmony_ci	struct fixed_phy_status fphy_status = {
228962306a36Sopenharmony_ci		.link = 1,
229062306a36Sopenharmony_ci		.speed = SPEED_1000,
229162306a36Sopenharmony_ci		.duplex = DUPLEX_FULL,
229262306a36Sopenharmony_ci	};
229362306a36Sopenharmony_ci	struct phy_device *phydev;
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	phydev = phy_find_first(dev->mdiobus);
229662306a36Sopenharmony_ci	if (!phydev) {
229762306a36Sopenharmony_ci		netdev_dbg(dev->net, "PHY Not Found!! Registering Fixed PHY\n");
229862306a36Sopenharmony_ci		phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
229962306a36Sopenharmony_ci		if (IS_ERR(phydev)) {
230062306a36Sopenharmony_ci			netdev_err(dev->net, "No PHY/fixed_PHY found\n");
230162306a36Sopenharmony_ci			return NULL;
230262306a36Sopenharmony_ci		}
230362306a36Sopenharmony_ci		netdev_dbg(dev->net, "Registered FIXED PHY\n");
230462306a36Sopenharmony_ci		dev->interface = PHY_INTERFACE_MODE_RGMII;
230562306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, MAC_RGMII_ID,
230662306a36Sopenharmony_ci					MAC_RGMII_ID_TXC_DELAY_EN_);
230762306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, RGMII_TX_BYP_DLL, 0x3D00);
230862306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, HW_CFG, &buf);
230962306a36Sopenharmony_ci		buf |= HW_CFG_CLK125_EN_;
231062306a36Sopenharmony_ci		buf |= HW_CFG_REFCLK25_EN_;
231162306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, HW_CFG, buf);
231262306a36Sopenharmony_ci	} else {
231362306a36Sopenharmony_ci		if (!phydev->drv) {
231462306a36Sopenharmony_ci			netdev_err(dev->net, "no PHY driver found\n");
231562306a36Sopenharmony_ci			return NULL;
231662306a36Sopenharmony_ci		}
231762306a36Sopenharmony_ci		dev->interface = PHY_INTERFACE_MODE_RGMII;
231862306a36Sopenharmony_ci		/* external PHY fixup for KSZ9031RNX */
231962306a36Sopenharmony_ci		ret = phy_register_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0,
232062306a36Sopenharmony_ci						 ksz9031rnx_fixup);
232162306a36Sopenharmony_ci		if (ret < 0) {
232262306a36Sopenharmony_ci			netdev_err(dev->net, "Failed to register fixup for PHY_KSZ9031RNX\n");
232362306a36Sopenharmony_ci			return NULL;
232462306a36Sopenharmony_ci		}
232562306a36Sopenharmony_ci		/* external PHY fixup for LAN8835 */
232662306a36Sopenharmony_ci		ret = phy_register_fixup_for_uid(PHY_LAN8835, 0xfffffff0,
232762306a36Sopenharmony_ci						 lan8835_fixup);
232862306a36Sopenharmony_ci		if (ret < 0) {
232962306a36Sopenharmony_ci			netdev_err(dev->net, "Failed to register fixup for PHY_LAN8835\n");
233062306a36Sopenharmony_ci			return NULL;
233162306a36Sopenharmony_ci		}
233262306a36Sopenharmony_ci		/* add more external PHY fixup here if needed */
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci		phydev->is_internal = false;
233562306a36Sopenharmony_ci	}
233662306a36Sopenharmony_ci	return phydev;
233762306a36Sopenharmony_ci}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_cistatic int lan78xx_phy_init(struct lan78xx_net *dev)
234062306a36Sopenharmony_ci{
234162306a36Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, };
234262306a36Sopenharmony_ci	int ret;
234362306a36Sopenharmony_ci	u32 mii_adv;
234462306a36Sopenharmony_ci	struct phy_device *phydev;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	switch (dev->chipid) {
234762306a36Sopenharmony_ci	case ID_REV_CHIP_ID_7801_:
234862306a36Sopenharmony_ci		phydev = lan7801_phy_init(dev);
234962306a36Sopenharmony_ci		if (!phydev) {
235062306a36Sopenharmony_ci			netdev_err(dev->net, "lan7801: PHY Init Failed");
235162306a36Sopenharmony_ci			return -EIO;
235262306a36Sopenharmony_ci		}
235362306a36Sopenharmony_ci		break;
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	case ID_REV_CHIP_ID_7800_:
235662306a36Sopenharmony_ci	case ID_REV_CHIP_ID_7850_:
235762306a36Sopenharmony_ci		phydev = phy_find_first(dev->mdiobus);
235862306a36Sopenharmony_ci		if (!phydev) {
235962306a36Sopenharmony_ci			netdev_err(dev->net, "no PHY found\n");
236062306a36Sopenharmony_ci			return -EIO;
236162306a36Sopenharmony_ci		}
236262306a36Sopenharmony_ci		phydev->is_internal = true;
236362306a36Sopenharmony_ci		dev->interface = PHY_INTERFACE_MODE_GMII;
236462306a36Sopenharmony_ci		break;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	default:
236762306a36Sopenharmony_ci		netdev_err(dev->net, "Unknown CHIP ID found\n");
236862306a36Sopenharmony_ci		return -EIO;
236962306a36Sopenharmony_ci	}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	/* if phyirq is not set, use polling mode in phylib */
237262306a36Sopenharmony_ci	if (dev->domain_data.phyirq > 0)
237362306a36Sopenharmony_ci		phydev->irq = dev->domain_data.phyirq;
237462306a36Sopenharmony_ci	else
237562306a36Sopenharmony_ci		phydev->irq = PHY_POLL;
237662306a36Sopenharmony_ci	netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq);
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	/* set to AUTOMDIX */
237962306a36Sopenharmony_ci	phydev->mdix = ETH_TP_MDI_AUTO;
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	ret = phy_connect_direct(dev->net, phydev,
238262306a36Sopenharmony_ci				 lan78xx_link_status_change,
238362306a36Sopenharmony_ci				 dev->interface);
238462306a36Sopenharmony_ci	if (ret) {
238562306a36Sopenharmony_ci		netdev_err(dev->net, "can't attach PHY to %s\n",
238662306a36Sopenharmony_ci			   dev->mdiobus->id);
238762306a36Sopenharmony_ci		if (dev->chipid == ID_REV_CHIP_ID_7801_) {
238862306a36Sopenharmony_ci			if (phy_is_pseudo_fixed_link(phydev)) {
238962306a36Sopenharmony_ci				fixed_phy_unregister(phydev);
239062306a36Sopenharmony_ci			} else {
239162306a36Sopenharmony_ci				phy_unregister_fixup_for_uid(PHY_KSZ9031RNX,
239262306a36Sopenharmony_ci							     0xfffffff0);
239362306a36Sopenharmony_ci				phy_unregister_fixup_for_uid(PHY_LAN8835,
239462306a36Sopenharmony_ci							     0xfffffff0);
239562306a36Sopenharmony_ci			}
239662306a36Sopenharmony_ci		}
239762306a36Sopenharmony_ci		return -EIO;
239862306a36Sopenharmony_ci	}
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	/* MAC doesn't support 1000T Half */
240162306a36Sopenharmony_ci	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	/* support both flow controls */
240462306a36Sopenharmony_ci	dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX);
240562306a36Sopenharmony_ci	linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
240662306a36Sopenharmony_ci			   phydev->advertising);
240762306a36Sopenharmony_ci	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
240862306a36Sopenharmony_ci			   phydev->advertising);
240962306a36Sopenharmony_ci	mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control);
241062306a36Sopenharmony_ci	mii_adv_to_linkmode_adv_t(fc, mii_adv);
241162306a36Sopenharmony_ci	linkmode_or(phydev->advertising, fc, phydev->advertising);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	if (phydev->mdio.dev.of_node) {
241462306a36Sopenharmony_ci		u32 reg;
241562306a36Sopenharmony_ci		int len;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci		len = of_property_count_elems_of_size(phydev->mdio.dev.of_node,
241862306a36Sopenharmony_ci						      "microchip,led-modes",
241962306a36Sopenharmony_ci						      sizeof(u32));
242062306a36Sopenharmony_ci		if (len >= 0) {
242162306a36Sopenharmony_ci			/* Ensure the appropriate LEDs are enabled */
242262306a36Sopenharmony_ci			lan78xx_read_reg(dev, HW_CFG, &reg);
242362306a36Sopenharmony_ci			reg &= ~(HW_CFG_LED0_EN_ |
242462306a36Sopenharmony_ci				 HW_CFG_LED1_EN_ |
242562306a36Sopenharmony_ci				 HW_CFG_LED2_EN_ |
242662306a36Sopenharmony_ci				 HW_CFG_LED3_EN_);
242762306a36Sopenharmony_ci			reg |= (len > 0) * HW_CFG_LED0_EN_ |
242862306a36Sopenharmony_ci				(len > 1) * HW_CFG_LED1_EN_ |
242962306a36Sopenharmony_ci				(len > 2) * HW_CFG_LED2_EN_ |
243062306a36Sopenharmony_ci				(len > 3) * HW_CFG_LED3_EN_;
243162306a36Sopenharmony_ci			lan78xx_write_reg(dev, HW_CFG, reg);
243262306a36Sopenharmony_ci		}
243362306a36Sopenharmony_ci	}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	genphy_config_aneg(phydev);
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	dev->fc_autoneg = phydev->autoneg;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	return 0;
244062306a36Sopenharmony_ci}
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_cistatic int lan78xx_set_rx_max_frame_length(struct lan78xx_net *dev, int size)
244362306a36Sopenharmony_ci{
244462306a36Sopenharmony_ci	u32 buf;
244562306a36Sopenharmony_ci	bool rxenabled;
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	lan78xx_read_reg(dev, MAC_RX, &buf);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	rxenabled = ((buf & MAC_RX_RXEN_) != 0);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	if (rxenabled) {
245262306a36Sopenharmony_ci		buf &= ~MAC_RX_RXEN_;
245362306a36Sopenharmony_ci		lan78xx_write_reg(dev, MAC_RX, buf);
245462306a36Sopenharmony_ci	}
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	/* add 4 to size for FCS */
245762306a36Sopenharmony_ci	buf &= ~MAC_RX_MAX_SIZE_MASK_;
245862306a36Sopenharmony_ci	buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT_) & MAC_RX_MAX_SIZE_MASK_);
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	lan78xx_write_reg(dev, MAC_RX, buf);
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	if (rxenabled) {
246362306a36Sopenharmony_ci		buf |= MAC_RX_RXEN_;
246462306a36Sopenharmony_ci		lan78xx_write_reg(dev, MAC_RX, buf);
246562306a36Sopenharmony_ci	}
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci	return 0;
246862306a36Sopenharmony_ci}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_cistatic int unlink_urbs(struct lan78xx_net *dev, struct sk_buff_head *q)
247162306a36Sopenharmony_ci{
247262306a36Sopenharmony_ci	struct sk_buff *skb;
247362306a36Sopenharmony_ci	unsigned long flags;
247462306a36Sopenharmony_ci	int count = 0;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	spin_lock_irqsave(&q->lock, flags);
247762306a36Sopenharmony_ci	while (!skb_queue_empty(q)) {
247862306a36Sopenharmony_ci		struct skb_data	*entry;
247962306a36Sopenharmony_ci		struct urb *urb;
248062306a36Sopenharmony_ci		int ret;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci		skb_queue_walk(q, skb) {
248362306a36Sopenharmony_ci			entry = (struct skb_data *)skb->cb;
248462306a36Sopenharmony_ci			if (entry->state != unlink_start)
248562306a36Sopenharmony_ci				goto found;
248662306a36Sopenharmony_ci		}
248762306a36Sopenharmony_ci		break;
248862306a36Sopenharmony_cifound:
248962306a36Sopenharmony_ci		entry->state = unlink_start;
249062306a36Sopenharmony_ci		urb = entry->urb;
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci		/* Get reference count of the URB to avoid it to be
249362306a36Sopenharmony_ci		 * freed during usb_unlink_urb, which may trigger
249462306a36Sopenharmony_ci		 * use-after-free problem inside usb_unlink_urb since
249562306a36Sopenharmony_ci		 * usb_unlink_urb is always racing with .complete
249662306a36Sopenharmony_ci		 * handler(include defer_bh).
249762306a36Sopenharmony_ci		 */
249862306a36Sopenharmony_ci		usb_get_urb(urb);
249962306a36Sopenharmony_ci		spin_unlock_irqrestore(&q->lock, flags);
250062306a36Sopenharmony_ci		/* during some PM-driven resume scenarios,
250162306a36Sopenharmony_ci		 * these (async) unlinks complete immediately
250262306a36Sopenharmony_ci		 */
250362306a36Sopenharmony_ci		ret = usb_unlink_urb(urb);
250462306a36Sopenharmony_ci		if (ret != -EINPROGRESS && ret != 0)
250562306a36Sopenharmony_ci			netdev_dbg(dev->net, "unlink urb err, %d\n", ret);
250662306a36Sopenharmony_ci		else
250762306a36Sopenharmony_ci			count++;
250862306a36Sopenharmony_ci		usb_put_urb(urb);
250962306a36Sopenharmony_ci		spin_lock_irqsave(&q->lock, flags);
251062306a36Sopenharmony_ci	}
251162306a36Sopenharmony_ci	spin_unlock_irqrestore(&q->lock, flags);
251262306a36Sopenharmony_ci	return count;
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic int lan78xx_change_mtu(struct net_device *netdev, int new_mtu)
251662306a36Sopenharmony_ci{
251762306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
251862306a36Sopenharmony_ci	int max_frame_len = RX_MAX_FRAME_LEN(new_mtu);
251962306a36Sopenharmony_ci	int ret;
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	/* no second zero-length packet read wanted after mtu-sized packets */
252262306a36Sopenharmony_ci	if ((max_frame_len % dev->maxpacket) == 0)
252362306a36Sopenharmony_ci		return -EDOM;
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
252662306a36Sopenharmony_ci	if (ret < 0)
252762306a36Sopenharmony_ci		return ret;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	ret = lan78xx_set_rx_max_frame_length(dev, max_frame_len);
253062306a36Sopenharmony_ci	if (!ret)
253162306a36Sopenharmony_ci		netdev->mtu = new_mtu;
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	return ret;
253662306a36Sopenharmony_ci}
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_cistatic int lan78xx_set_mac_addr(struct net_device *netdev, void *p)
253962306a36Sopenharmony_ci{
254062306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
254162306a36Sopenharmony_ci	struct sockaddr *addr = p;
254262306a36Sopenharmony_ci	u32 addr_lo, addr_hi;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	if (netif_running(netdev))
254562306a36Sopenharmony_ci		return -EBUSY;
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
254862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	eth_hw_addr_set(netdev, addr->sa_data);
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	addr_lo = netdev->dev_addr[0] |
255362306a36Sopenharmony_ci		  netdev->dev_addr[1] << 8 |
255462306a36Sopenharmony_ci		  netdev->dev_addr[2] << 16 |
255562306a36Sopenharmony_ci		  netdev->dev_addr[3] << 24;
255662306a36Sopenharmony_ci	addr_hi = netdev->dev_addr[4] |
255762306a36Sopenharmony_ci		  netdev->dev_addr[5] << 8;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	lan78xx_write_reg(dev, RX_ADDRL, addr_lo);
256062306a36Sopenharmony_ci	lan78xx_write_reg(dev, RX_ADDRH, addr_hi);
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	/* Added to support MAC address changes */
256362306a36Sopenharmony_ci	lan78xx_write_reg(dev, MAF_LO(0), addr_lo);
256462306a36Sopenharmony_ci	lan78xx_write_reg(dev, MAF_HI(0), addr_hi | MAF_HI_VALID_);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	return 0;
256762306a36Sopenharmony_ci}
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci/* Enable or disable Rx checksum offload engine */
257062306a36Sopenharmony_cistatic int lan78xx_set_features(struct net_device *netdev,
257162306a36Sopenharmony_ci				netdev_features_t features)
257262306a36Sopenharmony_ci{
257362306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
257462306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
257562306a36Sopenharmony_ci	unsigned long flags;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	if (features & NETIF_F_RXCSUM) {
258062306a36Sopenharmony_ci		pdata->rfe_ctl |= RFE_CTL_TCPUDP_COE_ | RFE_CTL_IP_COE_;
258162306a36Sopenharmony_ci		pdata->rfe_ctl |= RFE_CTL_ICMP_COE_ | RFE_CTL_IGMP_COE_;
258262306a36Sopenharmony_ci	} else {
258362306a36Sopenharmony_ci		pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_COE_ | RFE_CTL_IP_COE_);
258462306a36Sopenharmony_ci		pdata->rfe_ctl &= ~(RFE_CTL_ICMP_COE_ | RFE_CTL_IGMP_COE_);
258562306a36Sopenharmony_ci	}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
258862306a36Sopenharmony_ci		pdata->rfe_ctl |= RFE_CTL_VLAN_STRIP_;
258962306a36Sopenharmony_ci	else
259062306a36Sopenharmony_ci		pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_;
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
259362306a36Sopenharmony_ci		pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
259462306a36Sopenharmony_ci	else
259562306a36Sopenharmony_ci		pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	return 0;
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_cistatic void lan78xx_deferred_vlan_write(struct work_struct *param)
260562306a36Sopenharmony_ci{
260662306a36Sopenharmony_ci	struct lan78xx_priv *pdata =
260762306a36Sopenharmony_ci			container_of(param, struct lan78xx_priv, set_vlan);
260862306a36Sopenharmony_ci	struct lan78xx_net *dev = pdata->dev;
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0,
261162306a36Sopenharmony_ci			       DP_SEL_VHF_VLAN_LEN, pdata->vlan_table);
261262306a36Sopenharmony_ci}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_cistatic int lan78xx_vlan_rx_add_vid(struct net_device *netdev,
261562306a36Sopenharmony_ci				   __be16 proto, u16 vid)
261662306a36Sopenharmony_ci{
261762306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
261862306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
261962306a36Sopenharmony_ci	u16 vid_bit_index;
262062306a36Sopenharmony_ci	u16 vid_dword_index;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	vid_dword_index = (vid >> 5) & 0x7F;
262362306a36Sopenharmony_ci	vid_bit_index = vid & 0x1F;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	pdata->vlan_table[vid_dword_index] |= (1 << vid_bit_index);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	/* defer register writes to a sleepable context */
262862306a36Sopenharmony_ci	schedule_work(&pdata->set_vlan);
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	return 0;
263162306a36Sopenharmony_ci}
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_cistatic int lan78xx_vlan_rx_kill_vid(struct net_device *netdev,
263462306a36Sopenharmony_ci				    __be16 proto, u16 vid)
263562306a36Sopenharmony_ci{
263662306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
263762306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
263862306a36Sopenharmony_ci	u16 vid_bit_index;
263962306a36Sopenharmony_ci	u16 vid_dword_index;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	vid_dword_index = (vid >> 5) & 0x7F;
264262306a36Sopenharmony_ci	vid_bit_index = vid & 0x1F;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	pdata->vlan_table[vid_dword_index] &= ~(1 << vid_bit_index);
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	/* defer register writes to a sleepable context */
264762306a36Sopenharmony_ci	schedule_work(&pdata->set_vlan);
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	return 0;
265062306a36Sopenharmony_ci}
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_cistatic void lan78xx_init_ltm(struct lan78xx_net *dev)
265362306a36Sopenharmony_ci{
265462306a36Sopenharmony_ci	int ret;
265562306a36Sopenharmony_ci	u32 buf;
265662306a36Sopenharmony_ci	u32 regs[6] = { 0 };
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, USB_CFG1, &buf);
265962306a36Sopenharmony_ci	if (buf & USB_CFG1_LTM_ENABLE_) {
266062306a36Sopenharmony_ci		u8 temp[2];
266162306a36Sopenharmony_ci		/* Get values from EEPROM first */
266262306a36Sopenharmony_ci		if (lan78xx_read_eeprom(dev, 0x3F, 2, temp) == 0) {
266362306a36Sopenharmony_ci			if (temp[0] == 24) {
266462306a36Sopenharmony_ci				ret = lan78xx_read_raw_eeprom(dev,
266562306a36Sopenharmony_ci							      temp[1] * 2,
266662306a36Sopenharmony_ci							      24,
266762306a36Sopenharmony_ci							      (u8 *)regs);
266862306a36Sopenharmony_ci				if (ret < 0)
266962306a36Sopenharmony_ci					return;
267062306a36Sopenharmony_ci			}
267162306a36Sopenharmony_ci		} else if (lan78xx_read_otp(dev, 0x3F, 2, temp) == 0) {
267262306a36Sopenharmony_ci			if (temp[0] == 24) {
267362306a36Sopenharmony_ci				ret = lan78xx_read_raw_otp(dev,
267462306a36Sopenharmony_ci							   temp[1] * 2,
267562306a36Sopenharmony_ci							   24,
267662306a36Sopenharmony_ci							   (u8 *)regs);
267762306a36Sopenharmony_ci				if (ret < 0)
267862306a36Sopenharmony_ci					return;
267962306a36Sopenharmony_ci			}
268062306a36Sopenharmony_ci		}
268162306a36Sopenharmony_ci	}
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	lan78xx_write_reg(dev, LTM_BELT_IDLE0, regs[0]);
268462306a36Sopenharmony_ci	lan78xx_write_reg(dev, LTM_BELT_IDLE1, regs[1]);
268562306a36Sopenharmony_ci	lan78xx_write_reg(dev, LTM_BELT_ACT0, regs[2]);
268662306a36Sopenharmony_ci	lan78xx_write_reg(dev, LTM_BELT_ACT1, regs[3]);
268762306a36Sopenharmony_ci	lan78xx_write_reg(dev, LTM_INACTIVE0, regs[4]);
268862306a36Sopenharmony_ci	lan78xx_write_reg(dev, LTM_INACTIVE1, regs[5]);
268962306a36Sopenharmony_ci}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_cistatic int lan78xx_urb_config_init(struct lan78xx_net *dev)
269262306a36Sopenharmony_ci{
269362306a36Sopenharmony_ci	int result = 0;
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	switch (dev->udev->speed) {
269662306a36Sopenharmony_ci	case USB_SPEED_SUPER:
269762306a36Sopenharmony_ci		dev->rx_urb_size = RX_SS_URB_SIZE;
269862306a36Sopenharmony_ci		dev->tx_urb_size = TX_SS_URB_SIZE;
269962306a36Sopenharmony_ci		dev->n_rx_urbs = RX_SS_URB_NUM;
270062306a36Sopenharmony_ci		dev->n_tx_urbs = TX_SS_URB_NUM;
270162306a36Sopenharmony_ci		dev->bulk_in_delay = SS_BULK_IN_DELAY;
270262306a36Sopenharmony_ci		dev->burst_cap = SS_BURST_CAP_SIZE / SS_USB_PKT_SIZE;
270362306a36Sopenharmony_ci		break;
270462306a36Sopenharmony_ci	case USB_SPEED_HIGH:
270562306a36Sopenharmony_ci		dev->rx_urb_size = RX_HS_URB_SIZE;
270662306a36Sopenharmony_ci		dev->tx_urb_size = TX_HS_URB_SIZE;
270762306a36Sopenharmony_ci		dev->n_rx_urbs = RX_HS_URB_NUM;
270862306a36Sopenharmony_ci		dev->n_tx_urbs = TX_HS_URB_NUM;
270962306a36Sopenharmony_ci		dev->bulk_in_delay = HS_BULK_IN_DELAY;
271062306a36Sopenharmony_ci		dev->burst_cap = HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
271162306a36Sopenharmony_ci		break;
271262306a36Sopenharmony_ci	case USB_SPEED_FULL:
271362306a36Sopenharmony_ci		dev->rx_urb_size = RX_FS_URB_SIZE;
271462306a36Sopenharmony_ci		dev->tx_urb_size = TX_FS_URB_SIZE;
271562306a36Sopenharmony_ci		dev->n_rx_urbs = RX_FS_URB_NUM;
271662306a36Sopenharmony_ci		dev->n_tx_urbs = TX_FS_URB_NUM;
271762306a36Sopenharmony_ci		dev->bulk_in_delay = FS_BULK_IN_DELAY;
271862306a36Sopenharmony_ci		dev->burst_cap = FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
271962306a36Sopenharmony_ci		break;
272062306a36Sopenharmony_ci	default:
272162306a36Sopenharmony_ci		netdev_warn(dev->net, "USB bus speed not supported\n");
272262306a36Sopenharmony_ci		result = -EIO;
272362306a36Sopenharmony_ci		break;
272462306a36Sopenharmony_ci	}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	return result;
272762306a36Sopenharmony_ci}
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_cistatic int lan78xx_start_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enable)
273062306a36Sopenharmony_ci{
273162306a36Sopenharmony_ci	return lan78xx_update_reg(dev, reg, hw_enable, hw_enable);
273262306a36Sopenharmony_ci}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_cistatic int lan78xx_stop_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enabled,
273562306a36Sopenharmony_ci			   u32 hw_disabled)
273662306a36Sopenharmony_ci{
273762306a36Sopenharmony_ci	unsigned long timeout;
273862306a36Sopenharmony_ci	bool stopped = true;
273962306a36Sopenharmony_ci	int ret;
274062306a36Sopenharmony_ci	u32 buf;
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	/* Stop the h/w block (if not already stopped) */
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, reg, &buf);
274562306a36Sopenharmony_ci	if (ret < 0)
274662306a36Sopenharmony_ci		return ret;
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	if (buf & hw_enabled) {
274962306a36Sopenharmony_ci		buf &= ~hw_enabled;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, reg, buf);
275262306a36Sopenharmony_ci		if (ret < 0)
275362306a36Sopenharmony_ci			return ret;
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci		stopped = false;
275662306a36Sopenharmony_ci		timeout = jiffies + HW_DISABLE_TIMEOUT;
275762306a36Sopenharmony_ci		do  {
275862306a36Sopenharmony_ci			ret = lan78xx_read_reg(dev, reg, &buf);
275962306a36Sopenharmony_ci			if (ret < 0)
276062306a36Sopenharmony_ci				return ret;
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci			if (buf & hw_disabled)
276362306a36Sopenharmony_ci				stopped = true;
276462306a36Sopenharmony_ci			else
276562306a36Sopenharmony_ci				msleep(HW_DISABLE_DELAY_MS);
276662306a36Sopenharmony_ci		} while (!stopped && !time_after(jiffies, timeout));
276762306a36Sopenharmony_ci	}
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	ret = stopped ? 0 : -ETIME;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	return ret;
277262306a36Sopenharmony_ci}
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_cistatic int lan78xx_flush_fifo(struct lan78xx_net *dev, u32 reg, u32 fifo_flush)
277562306a36Sopenharmony_ci{
277662306a36Sopenharmony_ci	return lan78xx_update_reg(dev, reg, fifo_flush, fifo_flush);
277762306a36Sopenharmony_ci}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_cistatic int lan78xx_start_tx_path(struct lan78xx_net *dev)
278062306a36Sopenharmony_ci{
278162306a36Sopenharmony_ci	int ret;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	netif_dbg(dev, drv, dev->net, "start tx path");
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	/* Start the MAC transmitter */
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	ret = lan78xx_start_hw(dev, MAC_TX, MAC_TX_TXEN_);
278862306a36Sopenharmony_ci	if (ret < 0)
278962306a36Sopenharmony_ci		return ret;
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	/* Start the Tx FIFO */
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci	ret = lan78xx_start_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_);
279462306a36Sopenharmony_ci	if (ret < 0)
279562306a36Sopenharmony_ci		return ret;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	return 0;
279862306a36Sopenharmony_ci}
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_cistatic int lan78xx_stop_tx_path(struct lan78xx_net *dev)
280162306a36Sopenharmony_ci{
280262306a36Sopenharmony_ci	int ret;
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	netif_dbg(dev, drv, dev->net, "stop tx path");
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	/* Stop the Tx FIFO */
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	ret = lan78xx_stop_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_, FCT_TX_CTL_DIS_);
280962306a36Sopenharmony_ci	if (ret < 0)
281062306a36Sopenharmony_ci		return ret;
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	/* Stop the MAC transmitter */
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	ret = lan78xx_stop_hw(dev, MAC_TX, MAC_TX_TXEN_, MAC_TX_TXD_);
281562306a36Sopenharmony_ci	if (ret < 0)
281662306a36Sopenharmony_ci		return ret;
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	return 0;
281962306a36Sopenharmony_ci}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci/* The caller must ensure the Tx path is stopped before calling
282262306a36Sopenharmony_ci * lan78xx_flush_tx_fifo().
282362306a36Sopenharmony_ci */
282462306a36Sopenharmony_cistatic int lan78xx_flush_tx_fifo(struct lan78xx_net *dev)
282562306a36Sopenharmony_ci{
282662306a36Sopenharmony_ci	return lan78xx_flush_fifo(dev, FCT_TX_CTL, FCT_TX_CTL_RST_);
282762306a36Sopenharmony_ci}
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_cistatic int lan78xx_start_rx_path(struct lan78xx_net *dev)
283062306a36Sopenharmony_ci{
283162306a36Sopenharmony_ci	int ret;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	netif_dbg(dev, drv, dev->net, "start rx path");
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	/* Start the Rx FIFO */
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci	ret = lan78xx_start_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_);
283862306a36Sopenharmony_ci	if (ret < 0)
283962306a36Sopenharmony_ci		return ret;
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	/* Start the MAC receiver*/
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	ret = lan78xx_start_hw(dev, MAC_RX, MAC_RX_RXEN_);
284462306a36Sopenharmony_ci	if (ret < 0)
284562306a36Sopenharmony_ci		return ret;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	return 0;
284862306a36Sopenharmony_ci}
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_cistatic int lan78xx_stop_rx_path(struct lan78xx_net *dev)
285162306a36Sopenharmony_ci{
285262306a36Sopenharmony_ci	int ret;
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	netif_dbg(dev, drv, dev->net, "stop rx path");
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	/* Stop the MAC receiver */
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	ret = lan78xx_stop_hw(dev, MAC_RX, MAC_RX_RXEN_, MAC_RX_RXD_);
285962306a36Sopenharmony_ci	if (ret < 0)
286062306a36Sopenharmony_ci		return ret;
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci	/* Stop the Rx FIFO */
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	ret = lan78xx_stop_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_, FCT_RX_CTL_DIS_);
286562306a36Sopenharmony_ci	if (ret < 0)
286662306a36Sopenharmony_ci		return ret;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	return 0;
286962306a36Sopenharmony_ci}
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci/* The caller must ensure the Rx path is stopped before calling
287262306a36Sopenharmony_ci * lan78xx_flush_rx_fifo().
287362306a36Sopenharmony_ci */
287462306a36Sopenharmony_cistatic int lan78xx_flush_rx_fifo(struct lan78xx_net *dev)
287562306a36Sopenharmony_ci{
287662306a36Sopenharmony_ci	return lan78xx_flush_fifo(dev, FCT_RX_CTL, FCT_RX_CTL_RST_);
287762306a36Sopenharmony_ci}
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_cistatic int lan78xx_reset(struct lan78xx_net *dev)
288062306a36Sopenharmony_ci{
288162306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
288262306a36Sopenharmony_ci	unsigned long timeout;
288362306a36Sopenharmony_ci	int ret;
288462306a36Sopenharmony_ci	u32 buf;
288562306a36Sopenharmony_ci	u8 sig;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, HW_CFG, &buf);
288862306a36Sopenharmony_ci	if (ret < 0)
288962306a36Sopenharmony_ci		return ret;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci	buf |= HW_CFG_LRST_;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, HW_CFG, buf);
289462306a36Sopenharmony_ci	if (ret < 0)
289562306a36Sopenharmony_ci		return ret;
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	timeout = jiffies + HZ;
289862306a36Sopenharmony_ci	do {
289962306a36Sopenharmony_ci		mdelay(1);
290062306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, HW_CFG, &buf);
290162306a36Sopenharmony_ci		if (ret < 0)
290262306a36Sopenharmony_ci			return ret;
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci		if (time_after(jiffies, timeout)) {
290562306a36Sopenharmony_ci			netdev_warn(dev->net,
290662306a36Sopenharmony_ci				    "timeout on completion of LiteReset");
290762306a36Sopenharmony_ci			ret = -ETIMEDOUT;
290862306a36Sopenharmony_ci			return ret;
290962306a36Sopenharmony_ci		}
291062306a36Sopenharmony_ci	} while (buf & HW_CFG_LRST_);
291162306a36Sopenharmony_ci
291262306a36Sopenharmony_ci	lan78xx_init_mac_address(dev);
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	/* save DEVID for later usage */
291562306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, ID_REV, &buf);
291662306a36Sopenharmony_ci	if (ret < 0)
291762306a36Sopenharmony_ci		return ret;
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	dev->chipid = (buf & ID_REV_CHIP_ID_MASK_) >> 16;
292062306a36Sopenharmony_ci	dev->chiprev = buf & ID_REV_CHIP_REV_MASK_;
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci	/* Respond to the IN token with a NAK */
292362306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
292462306a36Sopenharmony_ci	if (ret < 0)
292562306a36Sopenharmony_ci		return ret;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	buf |= USB_CFG_BIR_;
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, USB_CFG0, buf);
293062306a36Sopenharmony_ci	if (ret < 0)
293162306a36Sopenharmony_ci		return ret;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	/* Init LTM */
293462306a36Sopenharmony_ci	lan78xx_init_ltm(dev);
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, BURST_CAP, dev->burst_cap);
293762306a36Sopenharmony_ci	if (ret < 0)
293862306a36Sopenharmony_ci		return ret;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, BULK_IN_DLY, dev->bulk_in_delay);
294162306a36Sopenharmony_ci	if (ret < 0)
294262306a36Sopenharmony_ci		return ret;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, HW_CFG, &buf);
294562306a36Sopenharmony_ci	if (ret < 0)
294662306a36Sopenharmony_ci		return ret;
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci	buf |= HW_CFG_MEF_;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, HW_CFG, buf);
295162306a36Sopenharmony_ci	if (ret < 0)
295262306a36Sopenharmony_ci		return ret;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
295562306a36Sopenharmony_ci	if (ret < 0)
295662306a36Sopenharmony_ci		return ret;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	buf |= USB_CFG_BCE_;
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, USB_CFG0, buf);
296162306a36Sopenharmony_ci	if (ret < 0)
296262306a36Sopenharmony_ci		return ret;
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	/* set FIFO sizes */
296562306a36Sopenharmony_ci	buf = (MAX_RX_FIFO_SIZE - 512) / 512;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, FCT_RX_FIFO_END, buf);
296862306a36Sopenharmony_ci	if (ret < 0)
296962306a36Sopenharmony_ci		return ret;
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	buf = (MAX_TX_FIFO_SIZE - 512) / 512;
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, FCT_TX_FIFO_END, buf);
297462306a36Sopenharmony_ci	if (ret < 0)
297562306a36Sopenharmony_ci		return ret;
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_);
297862306a36Sopenharmony_ci	if (ret < 0)
297962306a36Sopenharmony_ci		return ret;
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, FLOW, 0);
298262306a36Sopenharmony_ci	if (ret < 0)
298362306a36Sopenharmony_ci		return ret;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, FCT_FLOW, 0);
298662306a36Sopenharmony_ci	if (ret < 0)
298762306a36Sopenharmony_ci		return ret;
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	/* Don't need rfe_ctl_lock during initialisation */
299062306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl);
299162306a36Sopenharmony_ci	if (ret < 0)
299262306a36Sopenharmony_ci		return ret;
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	pdata->rfe_ctl |= RFE_CTL_BCAST_EN_ | RFE_CTL_DA_PERFECT_;
299562306a36Sopenharmony_ci
299662306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
299762306a36Sopenharmony_ci	if (ret < 0)
299862306a36Sopenharmony_ci		return ret;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	/* Enable or disable checksum offload engines */
300162306a36Sopenharmony_ci	ret = lan78xx_set_features(dev->net, dev->net->features);
300262306a36Sopenharmony_ci	if (ret < 0)
300362306a36Sopenharmony_ci		return ret;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	lan78xx_set_multicast(dev->net);
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	/* reset PHY */
300862306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
300962306a36Sopenharmony_ci	if (ret < 0)
301062306a36Sopenharmony_ci		return ret;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	buf |= PMT_CTL_PHY_RST_;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, PMT_CTL, buf);
301562306a36Sopenharmony_ci	if (ret < 0)
301662306a36Sopenharmony_ci		return ret;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	timeout = jiffies + HZ;
301962306a36Sopenharmony_ci	do {
302062306a36Sopenharmony_ci		mdelay(1);
302162306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
302262306a36Sopenharmony_ci		if (ret < 0)
302362306a36Sopenharmony_ci			return ret;
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci		if (time_after(jiffies, timeout)) {
302662306a36Sopenharmony_ci			netdev_warn(dev->net, "timeout waiting for PHY Reset");
302762306a36Sopenharmony_ci			ret = -ETIMEDOUT;
302862306a36Sopenharmony_ci			return ret;
302962306a36Sopenharmony_ci		}
303062306a36Sopenharmony_ci	} while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_));
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, MAC_CR, &buf);
303362306a36Sopenharmony_ci	if (ret < 0)
303462306a36Sopenharmony_ci		return ret;
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci	/* LAN7801 only has RGMII mode */
303762306a36Sopenharmony_ci	if (dev->chipid == ID_REV_CHIP_ID_7801_)
303862306a36Sopenharmony_ci		buf &= ~MAC_CR_GMII_EN_;
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci	if (dev->chipid == ID_REV_CHIP_ID_7800_ ||
304162306a36Sopenharmony_ci	    dev->chipid == ID_REV_CHIP_ID_7850_) {
304262306a36Sopenharmony_ci		ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig);
304362306a36Sopenharmony_ci		if (!ret && sig != EEPROM_INDICATOR) {
304462306a36Sopenharmony_ci			/* Implies there is no external eeprom. Set mac speed */
304562306a36Sopenharmony_ci			netdev_info(dev->net, "No External EEPROM. Setting MAC Speed\n");
304662306a36Sopenharmony_ci			buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
304762306a36Sopenharmony_ci		}
304862306a36Sopenharmony_ci	}
304962306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, MAC_CR, buf);
305062306a36Sopenharmony_ci	if (ret < 0)
305162306a36Sopenharmony_ci		return ret;
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	ret = lan78xx_set_rx_max_frame_length(dev,
305462306a36Sopenharmony_ci					      RX_MAX_FRAME_LEN(dev->net->mtu));
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	return ret;
305762306a36Sopenharmony_ci}
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_cistatic void lan78xx_init_stats(struct lan78xx_net *dev)
306062306a36Sopenharmony_ci{
306162306a36Sopenharmony_ci	u32 *p;
306262306a36Sopenharmony_ci	int i;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	/* initialize for stats update
306562306a36Sopenharmony_ci	 * some counters are 20bits and some are 32bits
306662306a36Sopenharmony_ci	 */
306762306a36Sopenharmony_ci	p = (u32 *)&dev->stats.rollover_max;
306862306a36Sopenharmony_ci	for (i = 0; i < (sizeof(dev->stats.rollover_max) / (sizeof(u32))); i++)
306962306a36Sopenharmony_ci		p[i] = 0xFFFFF;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	dev->stats.rollover_max.rx_unicast_byte_count = 0xFFFFFFFF;
307262306a36Sopenharmony_ci	dev->stats.rollover_max.rx_broadcast_byte_count = 0xFFFFFFFF;
307362306a36Sopenharmony_ci	dev->stats.rollover_max.rx_multicast_byte_count = 0xFFFFFFFF;
307462306a36Sopenharmony_ci	dev->stats.rollover_max.eee_rx_lpi_transitions = 0xFFFFFFFF;
307562306a36Sopenharmony_ci	dev->stats.rollover_max.eee_rx_lpi_time = 0xFFFFFFFF;
307662306a36Sopenharmony_ci	dev->stats.rollover_max.tx_unicast_byte_count = 0xFFFFFFFF;
307762306a36Sopenharmony_ci	dev->stats.rollover_max.tx_broadcast_byte_count = 0xFFFFFFFF;
307862306a36Sopenharmony_ci	dev->stats.rollover_max.tx_multicast_byte_count = 0xFFFFFFFF;
307962306a36Sopenharmony_ci	dev->stats.rollover_max.eee_tx_lpi_transitions = 0xFFFFFFFF;
308062306a36Sopenharmony_ci	dev->stats.rollover_max.eee_tx_lpi_time = 0xFFFFFFFF;
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	set_bit(EVENT_STAT_UPDATE, &dev->flags);
308362306a36Sopenharmony_ci}
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_cistatic int lan78xx_open(struct net_device *net)
308662306a36Sopenharmony_ci{
308762306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
308862306a36Sopenharmony_ci	int ret;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	netif_dbg(dev, ifup, dev->net, "open device");
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci	ret = usb_autopm_get_interface(dev->intf);
309362306a36Sopenharmony_ci	if (ret < 0)
309462306a36Sopenharmony_ci		return ret;
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci	mutex_lock(&dev->dev_mutex);
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	phy_start(net->phydev);
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci	netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	/* for Link Check */
310362306a36Sopenharmony_ci	if (dev->urb_intr) {
310462306a36Sopenharmony_ci		ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL);
310562306a36Sopenharmony_ci		if (ret < 0) {
310662306a36Sopenharmony_ci			netif_err(dev, ifup, dev->net,
310762306a36Sopenharmony_ci				  "intr submit %d\n", ret);
310862306a36Sopenharmony_ci			goto done;
310962306a36Sopenharmony_ci		}
311062306a36Sopenharmony_ci	}
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci	ret = lan78xx_flush_rx_fifo(dev);
311362306a36Sopenharmony_ci	if (ret < 0)
311462306a36Sopenharmony_ci		goto done;
311562306a36Sopenharmony_ci	ret = lan78xx_flush_tx_fifo(dev);
311662306a36Sopenharmony_ci	if (ret < 0)
311762306a36Sopenharmony_ci		goto done;
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	ret = lan78xx_start_tx_path(dev);
312062306a36Sopenharmony_ci	if (ret < 0)
312162306a36Sopenharmony_ci		goto done;
312262306a36Sopenharmony_ci	ret = lan78xx_start_rx_path(dev);
312362306a36Sopenharmony_ci	if (ret < 0)
312462306a36Sopenharmony_ci		goto done;
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci	lan78xx_init_stats(dev);
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci	set_bit(EVENT_DEV_OPEN, &dev->flags);
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	netif_start_queue(net);
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	dev->link_on = false;
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci	napi_enable(&dev->napi);
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	lan78xx_defer_kevent(dev, EVENT_LINK_RESET);
313762306a36Sopenharmony_cidone:
313862306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	if (ret < 0)
314162306a36Sopenharmony_ci		usb_autopm_put_interface(dev->intf);
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	return ret;
314462306a36Sopenharmony_ci}
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_cistatic void lan78xx_terminate_urbs(struct lan78xx_net *dev)
314762306a36Sopenharmony_ci{
314862306a36Sopenharmony_ci	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
314962306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
315062306a36Sopenharmony_ci	int temp;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	/* ensure there are no more active urbs */
315362306a36Sopenharmony_ci	add_wait_queue(&unlink_wakeup, &wait);
315462306a36Sopenharmony_ci	set_current_state(TASK_UNINTERRUPTIBLE);
315562306a36Sopenharmony_ci	dev->wait = &unlink_wakeup;
315662306a36Sopenharmony_ci	temp = unlink_urbs(dev, &dev->txq) + unlink_urbs(dev, &dev->rxq);
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	/* maybe wait for deletions to finish. */
315962306a36Sopenharmony_ci	while (!skb_queue_empty(&dev->rxq) ||
316062306a36Sopenharmony_ci	       !skb_queue_empty(&dev->txq)) {
316162306a36Sopenharmony_ci		schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS));
316262306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
316362306a36Sopenharmony_ci		netif_dbg(dev, ifdown, dev->net,
316462306a36Sopenharmony_ci			  "waited for %d urb completions", temp);
316562306a36Sopenharmony_ci	}
316662306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
316762306a36Sopenharmony_ci	dev->wait = NULL;
316862306a36Sopenharmony_ci	remove_wait_queue(&unlink_wakeup, &wait);
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	/* empty Rx done, Rx overflow and Tx pend queues
317162306a36Sopenharmony_ci	 */
317262306a36Sopenharmony_ci	while (!skb_queue_empty(&dev->rxq_done)) {
317362306a36Sopenharmony_ci		struct sk_buff *skb = skb_dequeue(&dev->rxq_done);
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci		lan78xx_release_rx_buf(dev, skb);
317662306a36Sopenharmony_ci	}
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	skb_queue_purge(&dev->rxq_overflow);
317962306a36Sopenharmony_ci	skb_queue_purge(&dev->txq_pend);
318062306a36Sopenharmony_ci}
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_cistatic int lan78xx_stop(struct net_device *net)
318362306a36Sopenharmony_ci{
318462306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	netif_dbg(dev, ifup, dev->net, "stop device");
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	mutex_lock(&dev->dev_mutex);
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci	if (timer_pending(&dev->stat_monitor))
319162306a36Sopenharmony_ci		del_timer_sync(&dev->stat_monitor);
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	clear_bit(EVENT_DEV_OPEN, &dev->flags);
319462306a36Sopenharmony_ci	netif_stop_queue(net);
319562306a36Sopenharmony_ci	napi_disable(&dev->napi);
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	lan78xx_terminate_urbs(dev);
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	netif_info(dev, ifdown, dev->net,
320062306a36Sopenharmony_ci		   "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n",
320162306a36Sopenharmony_ci		   net->stats.rx_packets, net->stats.tx_packets,
320262306a36Sopenharmony_ci		   net->stats.rx_errors, net->stats.tx_errors);
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	/* ignore errors that occur stopping the Tx and Rx data paths */
320562306a36Sopenharmony_ci	lan78xx_stop_tx_path(dev);
320662306a36Sopenharmony_ci	lan78xx_stop_rx_path(dev);
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	if (net->phydev)
320962306a36Sopenharmony_ci		phy_stop(net->phydev);
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	usb_kill_urb(dev->urb_intr);
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_ci	/* deferred work (task, timer, softirq) must also stop.
321462306a36Sopenharmony_ci	 * can't flush_scheduled_work() until we drop rtnl (later),
321562306a36Sopenharmony_ci	 * else workers could deadlock; so make workers a NOP.
321662306a36Sopenharmony_ci	 */
321762306a36Sopenharmony_ci	clear_bit(EVENT_TX_HALT, &dev->flags);
321862306a36Sopenharmony_ci	clear_bit(EVENT_RX_HALT, &dev->flags);
321962306a36Sopenharmony_ci	clear_bit(EVENT_LINK_RESET, &dev->flags);
322062306a36Sopenharmony_ci	clear_bit(EVENT_STAT_UPDATE, &dev->flags);
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	cancel_delayed_work_sync(&dev->wq);
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	return 0;
322962306a36Sopenharmony_ci}
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_cistatic enum skb_state defer_bh(struct lan78xx_net *dev, struct sk_buff *skb,
323262306a36Sopenharmony_ci			       struct sk_buff_head *list, enum skb_state state)
323362306a36Sopenharmony_ci{
323462306a36Sopenharmony_ci	unsigned long flags;
323562306a36Sopenharmony_ci	enum skb_state old_state;
323662306a36Sopenharmony_ci	struct skb_data *entry = (struct skb_data *)skb->cb;
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	spin_lock_irqsave(&list->lock, flags);
323962306a36Sopenharmony_ci	old_state = entry->state;
324062306a36Sopenharmony_ci	entry->state = state;
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci	__skb_unlink(skb, list);
324362306a36Sopenharmony_ci	spin_unlock(&list->lock);
324462306a36Sopenharmony_ci	spin_lock(&dev->rxq_done.lock);
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci	__skb_queue_tail(&dev->rxq_done, skb);
324762306a36Sopenharmony_ci	if (skb_queue_len(&dev->rxq_done) == 1)
324862306a36Sopenharmony_ci		napi_schedule(&dev->napi);
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rxq_done.lock, flags);
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	return old_state;
325362306a36Sopenharmony_ci}
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_cistatic void tx_complete(struct urb *urb)
325662306a36Sopenharmony_ci{
325762306a36Sopenharmony_ci	struct sk_buff *skb = (struct sk_buff *)urb->context;
325862306a36Sopenharmony_ci	struct skb_data *entry = (struct skb_data *)skb->cb;
325962306a36Sopenharmony_ci	struct lan78xx_net *dev = entry->dev;
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci	if (urb->status == 0) {
326262306a36Sopenharmony_ci		dev->net->stats.tx_packets += entry->num_of_packet;
326362306a36Sopenharmony_ci		dev->net->stats.tx_bytes += entry->length;
326462306a36Sopenharmony_ci	} else {
326562306a36Sopenharmony_ci		dev->net->stats.tx_errors += entry->num_of_packet;
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci		switch (urb->status) {
326862306a36Sopenharmony_ci		case -EPIPE:
326962306a36Sopenharmony_ci			lan78xx_defer_kevent(dev, EVENT_TX_HALT);
327062306a36Sopenharmony_ci			break;
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci		/* software-driven interface shutdown */
327362306a36Sopenharmony_ci		case -ECONNRESET:
327462306a36Sopenharmony_ci		case -ESHUTDOWN:
327562306a36Sopenharmony_ci			netif_dbg(dev, tx_err, dev->net,
327662306a36Sopenharmony_ci				  "tx err interface gone %d\n",
327762306a36Sopenharmony_ci				  entry->urb->status);
327862306a36Sopenharmony_ci			break;
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci		case -EPROTO:
328162306a36Sopenharmony_ci		case -ETIME:
328262306a36Sopenharmony_ci		case -EILSEQ:
328362306a36Sopenharmony_ci			netif_stop_queue(dev->net);
328462306a36Sopenharmony_ci			netif_dbg(dev, tx_err, dev->net,
328562306a36Sopenharmony_ci				  "tx err queue stopped %d\n",
328662306a36Sopenharmony_ci				  entry->urb->status);
328762306a36Sopenharmony_ci			break;
328862306a36Sopenharmony_ci		default:
328962306a36Sopenharmony_ci			netif_dbg(dev, tx_err, dev->net,
329062306a36Sopenharmony_ci				  "unknown tx err %d\n",
329162306a36Sopenharmony_ci				  entry->urb->status);
329262306a36Sopenharmony_ci			break;
329362306a36Sopenharmony_ci		}
329462306a36Sopenharmony_ci	}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci	usb_autopm_put_interface_async(dev->intf);
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	skb_unlink(skb, &dev->txq);
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	lan78xx_release_tx_buf(dev, skb);
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	/* Re-schedule NAPI if Tx data pending but no URBs in progress.
330362306a36Sopenharmony_ci	 */
330462306a36Sopenharmony_ci	if (skb_queue_empty(&dev->txq) &&
330562306a36Sopenharmony_ci	    !skb_queue_empty(&dev->txq_pend))
330662306a36Sopenharmony_ci		napi_schedule(&dev->napi);
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_cistatic void lan78xx_queue_skb(struct sk_buff_head *list,
331062306a36Sopenharmony_ci			      struct sk_buff *newsk, enum skb_state state)
331162306a36Sopenharmony_ci{
331262306a36Sopenharmony_ci	struct skb_data *entry = (struct skb_data *)newsk->cb;
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	__skb_queue_tail(list, newsk);
331562306a36Sopenharmony_ci	entry->state = state;
331662306a36Sopenharmony_ci}
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_cistatic unsigned int lan78xx_tx_urb_space(struct lan78xx_net *dev)
331962306a36Sopenharmony_ci{
332062306a36Sopenharmony_ci	return skb_queue_len(&dev->txq_free) * dev->tx_urb_size;
332162306a36Sopenharmony_ci}
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_cistatic unsigned int lan78xx_tx_pend_data_len(struct lan78xx_net *dev)
332462306a36Sopenharmony_ci{
332562306a36Sopenharmony_ci	return dev->tx_pend_data_len;
332662306a36Sopenharmony_ci}
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_cistatic void lan78xx_tx_pend_skb_add(struct lan78xx_net *dev,
332962306a36Sopenharmony_ci				    struct sk_buff *skb,
333062306a36Sopenharmony_ci				    unsigned int *tx_pend_data_len)
333162306a36Sopenharmony_ci{
333262306a36Sopenharmony_ci	unsigned long flags;
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_ci	spin_lock_irqsave(&dev->txq_pend.lock, flags);
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	__skb_queue_tail(&dev->txq_pend, skb);
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	dev->tx_pend_data_len += skb->len;
333962306a36Sopenharmony_ci	*tx_pend_data_len = dev->tx_pend_data_len;
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->txq_pend.lock, flags);
334262306a36Sopenharmony_ci}
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_cistatic void lan78xx_tx_pend_skb_head_add(struct lan78xx_net *dev,
334562306a36Sopenharmony_ci					 struct sk_buff *skb,
334662306a36Sopenharmony_ci					 unsigned int *tx_pend_data_len)
334762306a36Sopenharmony_ci{
334862306a36Sopenharmony_ci	unsigned long flags;
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	spin_lock_irqsave(&dev->txq_pend.lock, flags);
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	__skb_queue_head(&dev->txq_pend, skb);
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	dev->tx_pend_data_len += skb->len;
335562306a36Sopenharmony_ci	*tx_pend_data_len = dev->tx_pend_data_len;
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->txq_pend.lock, flags);
335862306a36Sopenharmony_ci}
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_cistatic void lan78xx_tx_pend_skb_get(struct lan78xx_net *dev,
336162306a36Sopenharmony_ci				    struct sk_buff **skb,
336262306a36Sopenharmony_ci				    unsigned int *tx_pend_data_len)
336362306a36Sopenharmony_ci{
336462306a36Sopenharmony_ci	unsigned long flags;
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_ci	spin_lock_irqsave(&dev->txq_pend.lock, flags);
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci	*skb = __skb_dequeue(&dev->txq_pend);
336962306a36Sopenharmony_ci	if (*skb)
337062306a36Sopenharmony_ci		dev->tx_pend_data_len -= (*skb)->len;
337162306a36Sopenharmony_ci	*tx_pend_data_len = dev->tx_pend_data_len;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->txq_pend.lock, flags);
337462306a36Sopenharmony_ci}
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_cistatic netdev_tx_t
337762306a36Sopenharmony_cilan78xx_start_xmit(struct sk_buff *skb, struct net_device *net)
337862306a36Sopenharmony_ci{
337962306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
338062306a36Sopenharmony_ci	unsigned int tx_pend_data_len;
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	if (test_bit(EVENT_DEV_ASLEEP, &dev->flags))
338362306a36Sopenharmony_ci		schedule_delayed_work(&dev->wq, 0);
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci	skb_tx_timestamp(skb);
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci	lan78xx_tx_pend_skb_add(dev, skb, &tx_pend_data_len);
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci	/* Set up a Tx URB if none is in progress */
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	if (skb_queue_empty(&dev->txq))
339262306a36Sopenharmony_ci		napi_schedule(&dev->napi);
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	/* Stop stack Tx queue if we have enough data to fill
339562306a36Sopenharmony_ci	 * all the free Tx URBs.
339662306a36Sopenharmony_ci	 */
339762306a36Sopenharmony_ci	if (tx_pend_data_len > lan78xx_tx_urb_space(dev)) {
339862306a36Sopenharmony_ci		netif_stop_queue(net);
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci		netif_dbg(dev, hw, dev->net, "tx data len: %u, urb space %u",
340162306a36Sopenharmony_ci			  tx_pend_data_len, lan78xx_tx_urb_space(dev));
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci		/* Kick off transmission of pending data */
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci		if (!skb_queue_empty(&dev->txq_free))
340662306a36Sopenharmony_ci			napi_schedule(&dev->napi);
340762306a36Sopenharmony_ci	}
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci	return NETDEV_TX_OK;
341062306a36Sopenharmony_ci}
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_cistatic int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf)
341362306a36Sopenharmony_ci{
341462306a36Sopenharmony_ci	struct lan78xx_priv *pdata = NULL;
341562306a36Sopenharmony_ci	int ret;
341662306a36Sopenharmony_ci	int i;
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci	dev->data[0] = (unsigned long)kzalloc(sizeof(*pdata), GFP_KERNEL);
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	pdata = (struct lan78xx_priv *)(dev->data[0]);
342162306a36Sopenharmony_ci	if (!pdata) {
342262306a36Sopenharmony_ci		netdev_warn(dev->net, "Unable to allocate lan78xx_priv");
342362306a36Sopenharmony_ci		return -ENOMEM;
342462306a36Sopenharmony_ci	}
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci	pdata->dev = dev;
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_ci	spin_lock_init(&pdata->rfe_ctl_lock);
342962306a36Sopenharmony_ci	mutex_init(&pdata->dataport_mutex);
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	INIT_WORK(&pdata->set_multicast, lan78xx_deferred_multicast_write);
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci	for (i = 0; i < DP_SEL_VHF_VLAN_LEN; i++)
343462306a36Sopenharmony_ci		pdata->vlan_table[i] = 0;
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci	INIT_WORK(&pdata->set_vlan, lan78xx_deferred_vlan_write);
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	dev->net->features = 0;
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_ci	if (DEFAULT_TX_CSUM_ENABLE)
344162306a36Sopenharmony_ci		dev->net->features |= NETIF_F_HW_CSUM;
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci	if (DEFAULT_RX_CSUM_ENABLE)
344462306a36Sopenharmony_ci		dev->net->features |= NETIF_F_RXCSUM;
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci	if (DEFAULT_TSO_CSUM_ENABLE)
344762306a36Sopenharmony_ci		dev->net->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG;
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci	if (DEFAULT_VLAN_RX_OFFLOAD)
345062306a36Sopenharmony_ci		dev->net->features |= NETIF_F_HW_VLAN_CTAG_RX;
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci	if (DEFAULT_VLAN_FILTER_ENABLE)
345362306a36Sopenharmony_ci		dev->net->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci	dev->net->hw_features = dev->net->features;
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	ret = lan78xx_setup_irq_domain(dev);
345862306a36Sopenharmony_ci	if (ret < 0) {
345962306a36Sopenharmony_ci		netdev_warn(dev->net,
346062306a36Sopenharmony_ci			    "lan78xx_setup_irq_domain() failed : %d", ret);
346162306a36Sopenharmony_ci		goto out1;
346262306a36Sopenharmony_ci	}
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci	/* Init all registers */
346562306a36Sopenharmony_ci	ret = lan78xx_reset(dev);
346662306a36Sopenharmony_ci	if (ret) {
346762306a36Sopenharmony_ci		netdev_warn(dev->net, "Registers INIT FAILED....");
346862306a36Sopenharmony_ci		goto out2;
346962306a36Sopenharmony_ci	}
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	ret = lan78xx_mdio_init(dev);
347262306a36Sopenharmony_ci	if (ret) {
347362306a36Sopenharmony_ci		netdev_warn(dev->net, "MDIO INIT FAILED.....");
347462306a36Sopenharmony_ci		goto out2;
347562306a36Sopenharmony_ci	}
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci	dev->net->flags |= IFF_MULTICAST;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	pdata->wol = WAKE_MAGIC;
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	return ret;
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ciout2:
348462306a36Sopenharmony_ci	lan78xx_remove_irq_domain(dev);
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ciout1:
348762306a36Sopenharmony_ci	netdev_warn(dev->net, "Bind routine FAILED");
348862306a36Sopenharmony_ci	cancel_work_sync(&pdata->set_multicast);
348962306a36Sopenharmony_ci	cancel_work_sync(&pdata->set_vlan);
349062306a36Sopenharmony_ci	kfree(pdata);
349162306a36Sopenharmony_ci	return ret;
349262306a36Sopenharmony_ci}
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_cistatic void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf)
349562306a36Sopenharmony_ci{
349662306a36Sopenharmony_ci	struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	lan78xx_remove_irq_domain(dev);
349962306a36Sopenharmony_ci
350062306a36Sopenharmony_ci	lan78xx_remove_mdio(dev);
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci	if (pdata) {
350362306a36Sopenharmony_ci		cancel_work_sync(&pdata->set_multicast);
350462306a36Sopenharmony_ci		cancel_work_sync(&pdata->set_vlan);
350562306a36Sopenharmony_ci		netif_dbg(dev, ifdown, dev->net, "free pdata");
350662306a36Sopenharmony_ci		kfree(pdata);
350762306a36Sopenharmony_ci		pdata = NULL;
350862306a36Sopenharmony_ci		dev->data[0] = 0;
350962306a36Sopenharmony_ci	}
351062306a36Sopenharmony_ci}
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_cistatic void lan78xx_rx_csum_offload(struct lan78xx_net *dev,
351362306a36Sopenharmony_ci				    struct sk_buff *skb,
351462306a36Sopenharmony_ci				    u32 rx_cmd_a, u32 rx_cmd_b)
351562306a36Sopenharmony_ci{
351662306a36Sopenharmony_ci	/* HW Checksum offload appears to be flawed if used when not stripping
351762306a36Sopenharmony_ci	 * VLAN headers. Drop back to S/W checksums under these conditions.
351862306a36Sopenharmony_ci	 */
351962306a36Sopenharmony_ci	if (!(dev->net->features & NETIF_F_RXCSUM) ||
352062306a36Sopenharmony_ci	    unlikely(rx_cmd_a & RX_CMD_A_ICSM_) ||
352162306a36Sopenharmony_ci	    ((rx_cmd_a & RX_CMD_A_FVTG_) &&
352262306a36Sopenharmony_ci	     !(dev->net->features & NETIF_F_HW_VLAN_CTAG_RX))) {
352362306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_NONE;
352462306a36Sopenharmony_ci	} else {
352562306a36Sopenharmony_ci		skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT_));
352662306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_COMPLETE;
352762306a36Sopenharmony_ci	}
352862306a36Sopenharmony_ci}
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_cistatic void lan78xx_rx_vlan_offload(struct lan78xx_net *dev,
353162306a36Sopenharmony_ci				    struct sk_buff *skb,
353262306a36Sopenharmony_ci				    u32 rx_cmd_a, u32 rx_cmd_b)
353362306a36Sopenharmony_ci{
353462306a36Sopenharmony_ci	if ((dev->net->features & NETIF_F_HW_VLAN_CTAG_RX) &&
353562306a36Sopenharmony_ci	    (rx_cmd_a & RX_CMD_A_FVTG_))
353662306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
353762306a36Sopenharmony_ci				       (rx_cmd_b & 0xffff));
353862306a36Sopenharmony_ci}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_cistatic void lan78xx_skb_return(struct lan78xx_net *dev, struct sk_buff *skb)
354162306a36Sopenharmony_ci{
354262306a36Sopenharmony_ci	dev->net->stats.rx_packets++;
354362306a36Sopenharmony_ci	dev->net->stats.rx_bytes += skb->len;
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev->net);
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
354862306a36Sopenharmony_ci		  skb->len + sizeof(struct ethhdr), skb->protocol);
354962306a36Sopenharmony_ci	memset(skb->cb, 0, sizeof(struct skb_data));
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	if (skb_defer_rx_timestamp(skb))
355262306a36Sopenharmony_ci		return;
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	napi_gro_receive(&dev->napi, skb);
355562306a36Sopenharmony_ci}
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_cistatic int lan78xx_rx(struct lan78xx_net *dev, struct sk_buff *skb,
355862306a36Sopenharmony_ci		      int budget, int *work_done)
355962306a36Sopenharmony_ci{
356062306a36Sopenharmony_ci	if (skb->len < RX_SKB_MIN_LEN)
356162306a36Sopenharmony_ci		return 0;
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci	/* Extract frames from the URB buffer and pass each one to
356462306a36Sopenharmony_ci	 * the stack in a new NAPI SKB.
356562306a36Sopenharmony_ci	 */
356662306a36Sopenharmony_ci	while (skb->len > 0) {
356762306a36Sopenharmony_ci		u32 rx_cmd_a, rx_cmd_b, align_count, size;
356862306a36Sopenharmony_ci		u16 rx_cmd_c;
356962306a36Sopenharmony_ci		unsigned char *packet;
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_ci		rx_cmd_a = get_unaligned_le32(skb->data);
357262306a36Sopenharmony_ci		skb_pull(skb, sizeof(rx_cmd_a));
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci		rx_cmd_b = get_unaligned_le32(skb->data);
357562306a36Sopenharmony_ci		skb_pull(skb, sizeof(rx_cmd_b));
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci		rx_cmd_c = get_unaligned_le16(skb->data);
357862306a36Sopenharmony_ci		skb_pull(skb, sizeof(rx_cmd_c));
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci		packet = skb->data;
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci		/* get the packet length */
358362306a36Sopenharmony_ci		size = (rx_cmd_a & RX_CMD_A_LEN_MASK_);
358462306a36Sopenharmony_ci		align_count = (4 - ((size + RXW_PADDING) % 4)) % 4;
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci		if (unlikely(size > skb->len)) {
358762306a36Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
358862306a36Sopenharmony_ci				  "size err rx_cmd_a=0x%08x\n",
358962306a36Sopenharmony_ci				  rx_cmd_a);
359062306a36Sopenharmony_ci			return 0;
359162306a36Sopenharmony_ci		}
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci		if (unlikely(rx_cmd_a & RX_CMD_A_RED_)) {
359462306a36Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
359562306a36Sopenharmony_ci				  "Error rx_cmd_a=0x%08x", rx_cmd_a);
359662306a36Sopenharmony_ci		} else {
359762306a36Sopenharmony_ci			u32 frame_len;
359862306a36Sopenharmony_ci			struct sk_buff *skb2;
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_ci			if (unlikely(size < ETH_FCS_LEN)) {
360162306a36Sopenharmony_ci				netif_dbg(dev, rx_err, dev->net,
360262306a36Sopenharmony_ci					  "size err rx_cmd_a=0x%08x\n",
360362306a36Sopenharmony_ci					  rx_cmd_a);
360462306a36Sopenharmony_ci				return 0;
360562306a36Sopenharmony_ci			}
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci			frame_len = size - ETH_FCS_LEN;
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci			skb2 = napi_alloc_skb(&dev->napi, frame_len);
361062306a36Sopenharmony_ci			if (!skb2)
361162306a36Sopenharmony_ci				return 0;
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci			memcpy(skb2->data, packet, frame_len);
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci			skb_put(skb2, frame_len);
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci			lan78xx_rx_csum_offload(dev, skb2, rx_cmd_a, rx_cmd_b);
361862306a36Sopenharmony_ci			lan78xx_rx_vlan_offload(dev, skb2, rx_cmd_a, rx_cmd_b);
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci			/* Processing of the URB buffer must complete once
362162306a36Sopenharmony_ci			 * it has started. If the NAPI work budget is exhausted
362262306a36Sopenharmony_ci			 * while frames remain they are added to the overflow
362362306a36Sopenharmony_ci			 * queue for delivery in the next NAPI polling cycle.
362462306a36Sopenharmony_ci			 */
362562306a36Sopenharmony_ci			if (*work_done < budget) {
362662306a36Sopenharmony_ci				lan78xx_skb_return(dev, skb2);
362762306a36Sopenharmony_ci				++(*work_done);
362862306a36Sopenharmony_ci			} else {
362962306a36Sopenharmony_ci				skb_queue_tail(&dev->rxq_overflow, skb2);
363062306a36Sopenharmony_ci			}
363162306a36Sopenharmony_ci		}
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci		skb_pull(skb, size);
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci		/* skip padding bytes before the next frame starts */
363662306a36Sopenharmony_ci		if (skb->len)
363762306a36Sopenharmony_ci			skb_pull(skb, align_count);
363862306a36Sopenharmony_ci	}
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci	return 1;
364162306a36Sopenharmony_ci}
364262306a36Sopenharmony_ci
364362306a36Sopenharmony_cistatic inline void rx_process(struct lan78xx_net *dev, struct sk_buff *skb,
364462306a36Sopenharmony_ci			      int budget, int *work_done)
364562306a36Sopenharmony_ci{
364662306a36Sopenharmony_ci	if (!lan78xx_rx(dev, skb, budget, work_done)) {
364762306a36Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "drop\n");
364862306a36Sopenharmony_ci		dev->net->stats.rx_errors++;
364962306a36Sopenharmony_ci	}
365062306a36Sopenharmony_ci}
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_cistatic void rx_complete(struct urb *urb)
365362306a36Sopenharmony_ci{
365462306a36Sopenharmony_ci	struct sk_buff	*skb = (struct sk_buff *)urb->context;
365562306a36Sopenharmony_ci	struct skb_data	*entry = (struct skb_data *)skb->cb;
365662306a36Sopenharmony_ci	struct lan78xx_net *dev = entry->dev;
365762306a36Sopenharmony_ci	int urb_status = urb->status;
365862306a36Sopenharmony_ci	enum skb_state state;
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	netif_dbg(dev, rx_status, dev->net,
366162306a36Sopenharmony_ci		  "rx done: status %d", urb->status);
366262306a36Sopenharmony_ci
366362306a36Sopenharmony_ci	skb_put(skb, urb->actual_length);
366462306a36Sopenharmony_ci	state = rx_done;
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	if (urb != entry->urb)
366762306a36Sopenharmony_ci		netif_warn(dev, rx_err, dev->net, "URB pointer mismatch");
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	switch (urb_status) {
367062306a36Sopenharmony_ci	case 0:
367162306a36Sopenharmony_ci		if (skb->len < RX_SKB_MIN_LEN) {
367262306a36Sopenharmony_ci			state = rx_cleanup;
367362306a36Sopenharmony_ci			dev->net->stats.rx_errors++;
367462306a36Sopenharmony_ci			dev->net->stats.rx_length_errors++;
367562306a36Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
367662306a36Sopenharmony_ci				  "rx length %d\n", skb->len);
367762306a36Sopenharmony_ci		}
367862306a36Sopenharmony_ci		usb_mark_last_busy(dev->udev);
367962306a36Sopenharmony_ci		break;
368062306a36Sopenharmony_ci	case -EPIPE:
368162306a36Sopenharmony_ci		dev->net->stats.rx_errors++;
368262306a36Sopenharmony_ci		lan78xx_defer_kevent(dev, EVENT_RX_HALT);
368362306a36Sopenharmony_ci		fallthrough;
368462306a36Sopenharmony_ci	case -ECONNRESET:				/* async unlink */
368562306a36Sopenharmony_ci	case -ESHUTDOWN:				/* hardware gone */
368662306a36Sopenharmony_ci		netif_dbg(dev, ifdown, dev->net,
368762306a36Sopenharmony_ci			  "rx shutdown, code %d\n", urb_status);
368862306a36Sopenharmony_ci		state = rx_cleanup;
368962306a36Sopenharmony_ci		break;
369062306a36Sopenharmony_ci	case -EPROTO:
369162306a36Sopenharmony_ci	case -ETIME:
369262306a36Sopenharmony_ci	case -EILSEQ:
369362306a36Sopenharmony_ci		dev->net->stats.rx_errors++;
369462306a36Sopenharmony_ci		state = rx_cleanup;
369562306a36Sopenharmony_ci		break;
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci	/* data overrun ... flush fifo? */
369862306a36Sopenharmony_ci	case -EOVERFLOW:
369962306a36Sopenharmony_ci		dev->net->stats.rx_over_errors++;
370062306a36Sopenharmony_ci		fallthrough;
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci	default:
370362306a36Sopenharmony_ci		state = rx_cleanup;
370462306a36Sopenharmony_ci		dev->net->stats.rx_errors++;
370562306a36Sopenharmony_ci		netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
370662306a36Sopenharmony_ci		break;
370762306a36Sopenharmony_ci	}
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci	state = defer_bh(dev, skb, &dev->rxq, state);
371062306a36Sopenharmony_ci}
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_cistatic int rx_submit(struct lan78xx_net *dev, struct sk_buff *skb, gfp_t flags)
371362306a36Sopenharmony_ci{
371462306a36Sopenharmony_ci	struct skb_data	*entry = (struct skb_data *)skb->cb;
371562306a36Sopenharmony_ci	size_t size = dev->rx_urb_size;
371662306a36Sopenharmony_ci	struct urb *urb = entry->urb;
371762306a36Sopenharmony_ci	unsigned long lockflags;
371862306a36Sopenharmony_ci	int ret = 0;
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci	usb_fill_bulk_urb(urb, dev->udev, dev->pipe_in,
372162306a36Sopenharmony_ci			  skb->data, size, rx_complete, skb);
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rxq.lock, lockflags);
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci	if (netif_device_present(dev->net) &&
372662306a36Sopenharmony_ci	    netif_running(dev->net) &&
372762306a36Sopenharmony_ci	    !test_bit(EVENT_RX_HALT, &dev->flags) &&
372862306a36Sopenharmony_ci	    !test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
372962306a36Sopenharmony_ci		ret = usb_submit_urb(urb, flags);
373062306a36Sopenharmony_ci		switch (ret) {
373162306a36Sopenharmony_ci		case 0:
373262306a36Sopenharmony_ci			lan78xx_queue_skb(&dev->rxq, skb, rx_start);
373362306a36Sopenharmony_ci			break;
373462306a36Sopenharmony_ci		case -EPIPE:
373562306a36Sopenharmony_ci			lan78xx_defer_kevent(dev, EVENT_RX_HALT);
373662306a36Sopenharmony_ci			break;
373762306a36Sopenharmony_ci		case -ENODEV:
373862306a36Sopenharmony_ci		case -ENOENT:
373962306a36Sopenharmony_ci			netif_dbg(dev, ifdown, dev->net, "device gone\n");
374062306a36Sopenharmony_ci			netif_device_detach(dev->net);
374162306a36Sopenharmony_ci			break;
374262306a36Sopenharmony_ci		case -EHOSTUNREACH:
374362306a36Sopenharmony_ci			ret = -ENOLINK;
374462306a36Sopenharmony_ci			napi_schedule(&dev->napi);
374562306a36Sopenharmony_ci			break;
374662306a36Sopenharmony_ci		default:
374762306a36Sopenharmony_ci			netif_dbg(dev, rx_err, dev->net,
374862306a36Sopenharmony_ci				  "rx submit, %d\n", ret);
374962306a36Sopenharmony_ci			napi_schedule(&dev->napi);
375062306a36Sopenharmony_ci			break;
375162306a36Sopenharmony_ci		}
375262306a36Sopenharmony_ci	} else {
375362306a36Sopenharmony_ci		netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
375462306a36Sopenharmony_ci		ret = -ENOLINK;
375562306a36Sopenharmony_ci	}
375662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rxq.lock, lockflags);
375762306a36Sopenharmony_ci
375862306a36Sopenharmony_ci	if (ret)
375962306a36Sopenharmony_ci		lan78xx_release_rx_buf(dev, skb);
376062306a36Sopenharmony_ci
376162306a36Sopenharmony_ci	return ret;
376262306a36Sopenharmony_ci}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_cistatic void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev)
376562306a36Sopenharmony_ci{
376662306a36Sopenharmony_ci	struct sk_buff *rx_buf;
376762306a36Sopenharmony_ci
376862306a36Sopenharmony_ci	/* Ensure the maximum number of Rx URBs is submitted
376962306a36Sopenharmony_ci	 */
377062306a36Sopenharmony_ci	while ((rx_buf = lan78xx_get_rx_buf(dev)) != NULL) {
377162306a36Sopenharmony_ci		if (rx_submit(dev, rx_buf, GFP_ATOMIC) != 0)
377262306a36Sopenharmony_ci			break;
377362306a36Sopenharmony_ci	}
377462306a36Sopenharmony_ci}
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_cistatic void lan78xx_rx_urb_resubmit(struct lan78xx_net *dev,
377762306a36Sopenharmony_ci				    struct sk_buff *rx_buf)
377862306a36Sopenharmony_ci{
377962306a36Sopenharmony_ci	/* reset SKB data pointers */
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci	rx_buf->data = rx_buf->head;
378262306a36Sopenharmony_ci	skb_reset_tail_pointer(rx_buf);
378362306a36Sopenharmony_ci	rx_buf->len = 0;
378462306a36Sopenharmony_ci	rx_buf->data_len = 0;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	rx_submit(dev, rx_buf, GFP_ATOMIC);
378762306a36Sopenharmony_ci}
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_cistatic void lan78xx_fill_tx_cmd_words(struct sk_buff *skb, u8 *buffer)
379062306a36Sopenharmony_ci{
379162306a36Sopenharmony_ci	u32 tx_cmd_a;
379262306a36Sopenharmony_ci	u32 tx_cmd_b;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN_MASK_) | TX_CMD_A_FCS_;
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL)
379762306a36Sopenharmony_ci		tx_cmd_a |= TX_CMD_A_IPE_ | TX_CMD_A_TPE_;
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ci	tx_cmd_b = 0;
380062306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
380162306a36Sopenharmony_ci		u16 mss = max(skb_shinfo(skb)->gso_size, TX_CMD_B_MSS_MIN_);
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci		tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT_) & TX_CMD_B_MSS_MASK_;
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci		tx_cmd_a |= TX_CMD_A_LSO_;
380662306a36Sopenharmony_ci	}
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
380962306a36Sopenharmony_ci		tx_cmd_a |= TX_CMD_A_IVTG_;
381062306a36Sopenharmony_ci		tx_cmd_b |= skb_vlan_tag_get(skb) & TX_CMD_B_VTAG_MASK_;
381162306a36Sopenharmony_ci	}
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	put_unaligned_le32(tx_cmd_a, buffer);
381462306a36Sopenharmony_ci	put_unaligned_le32(tx_cmd_b, buffer + 4);
381562306a36Sopenharmony_ci}
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_cistatic struct skb_data *lan78xx_tx_buf_fill(struct lan78xx_net *dev,
381862306a36Sopenharmony_ci					    struct sk_buff *tx_buf)
381962306a36Sopenharmony_ci{
382062306a36Sopenharmony_ci	struct skb_data *entry = (struct skb_data *)tx_buf->cb;
382162306a36Sopenharmony_ci	int remain = dev->tx_urb_size;
382262306a36Sopenharmony_ci	u8 *tx_data = tx_buf->data;
382362306a36Sopenharmony_ci	u32 urb_len = 0;
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_ci	entry->num_of_packet = 0;
382662306a36Sopenharmony_ci	entry->length = 0;
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	/* Work through the pending SKBs and copy the data of each SKB into
382962306a36Sopenharmony_ci	 * the URB buffer if there room for all the SKB data.
383062306a36Sopenharmony_ci	 *
383162306a36Sopenharmony_ci	 * There must be at least DST+SRC+TYPE in the SKB (with padding enabled)
383262306a36Sopenharmony_ci	 */
383362306a36Sopenharmony_ci	while (remain >= TX_SKB_MIN_LEN) {
383462306a36Sopenharmony_ci		unsigned int pending_bytes;
383562306a36Sopenharmony_ci		unsigned int align_bytes;
383662306a36Sopenharmony_ci		struct sk_buff *skb;
383762306a36Sopenharmony_ci		unsigned int len;
383862306a36Sopenharmony_ci
383962306a36Sopenharmony_ci		lan78xx_tx_pend_skb_get(dev, &skb, &pending_bytes);
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci		if (!skb)
384262306a36Sopenharmony_ci			break;
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci		align_bytes = (TX_ALIGNMENT - (urb_len % TX_ALIGNMENT)) %
384562306a36Sopenharmony_ci			      TX_ALIGNMENT;
384662306a36Sopenharmony_ci		len = align_bytes + TX_CMD_LEN + skb->len;
384762306a36Sopenharmony_ci		if (len > remain) {
384862306a36Sopenharmony_ci			lan78xx_tx_pend_skb_head_add(dev, skb, &pending_bytes);
384962306a36Sopenharmony_ci			break;
385062306a36Sopenharmony_ci		}
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_ci		tx_data += align_bytes;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci		lan78xx_fill_tx_cmd_words(skb, tx_data);
385562306a36Sopenharmony_ci		tx_data += TX_CMD_LEN;
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci		len = skb->len;
385862306a36Sopenharmony_ci		if (skb_copy_bits(skb, 0, tx_data, len) < 0) {
385962306a36Sopenharmony_ci			struct net_device_stats *stats = &dev->net->stats;
386062306a36Sopenharmony_ci
386162306a36Sopenharmony_ci			stats->tx_dropped++;
386262306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
386362306a36Sopenharmony_ci			tx_data -= TX_CMD_LEN;
386462306a36Sopenharmony_ci			continue;
386562306a36Sopenharmony_ci		}
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_ci		tx_data += len;
386862306a36Sopenharmony_ci		entry->length += len;
386962306a36Sopenharmony_ci		entry->num_of_packet += skb_shinfo(skb)->gso_segs ?: 1;
387062306a36Sopenharmony_ci
387162306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_ci		urb_len = (u32)(tx_data - (u8 *)tx_buf->data);
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci		remain = dev->tx_urb_size - urb_len;
387662306a36Sopenharmony_ci	}
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci	skb_put(tx_buf, urb_len);
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	return entry;
388162306a36Sopenharmony_ci}
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_cistatic void lan78xx_tx_bh(struct lan78xx_net *dev)
388462306a36Sopenharmony_ci{
388562306a36Sopenharmony_ci	int ret;
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	/* Start the stack Tx queue if it was stopped
388862306a36Sopenharmony_ci	 */
388962306a36Sopenharmony_ci	netif_tx_lock(dev->net);
389062306a36Sopenharmony_ci	if (netif_queue_stopped(dev->net)) {
389162306a36Sopenharmony_ci		if (lan78xx_tx_pend_data_len(dev) < lan78xx_tx_urb_space(dev))
389262306a36Sopenharmony_ci			netif_wake_queue(dev->net);
389362306a36Sopenharmony_ci	}
389462306a36Sopenharmony_ci	netif_tx_unlock(dev->net);
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci	/* Go through the Tx pending queue and set up URBs to transfer
389762306a36Sopenharmony_ci	 * the data to the device. Stop if no more pending data or URBs,
389862306a36Sopenharmony_ci	 * or if an error occurs when a URB is submitted.
389962306a36Sopenharmony_ci	 */
390062306a36Sopenharmony_ci	do {
390162306a36Sopenharmony_ci		struct skb_data *entry;
390262306a36Sopenharmony_ci		struct sk_buff *tx_buf;
390362306a36Sopenharmony_ci		unsigned long flags;
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci		if (skb_queue_empty(&dev->txq_pend))
390662306a36Sopenharmony_ci			break;
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci		tx_buf = lan78xx_get_tx_buf(dev);
390962306a36Sopenharmony_ci		if (!tx_buf)
391062306a36Sopenharmony_ci			break;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci		entry = lan78xx_tx_buf_fill(dev, tx_buf);
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_ci		spin_lock_irqsave(&dev->txq.lock, flags);
391562306a36Sopenharmony_ci		ret = usb_autopm_get_interface_async(dev->intf);
391662306a36Sopenharmony_ci		if (ret < 0) {
391762306a36Sopenharmony_ci			spin_unlock_irqrestore(&dev->txq.lock, flags);
391862306a36Sopenharmony_ci			goto out;
391962306a36Sopenharmony_ci		}
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci		usb_fill_bulk_urb(entry->urb, dev->udev, dev->pipe_out,
392262306a36Sopenharmony_ci				  tx_buf->data, tx_buf->len, tx_complete,
392362306a36Sopenharmony_ci				  tx_buf);
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_ci		if (tx_buf->len % dev->maxpacket == 0) {
392662306a36Sopenharmony_ci			/* send USB_ZERO_PACKET */
392762306a36Sopenharmony_ci			entry->urb->transfer_flags |= URB_ZERO_PACKET;
392862306a36Sopenharmony_ci		}
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_ci#ifdef CONFIG_PM
393162306a36Sopenharmony_ci		/* if device is asleep stop outgoing packet processing */
393262306a36Sopenharmony_ci		if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
393362306a36Sopenharmony_ci			usb_anchor_urb(entry->urb, &dev->deferred);
393462306a36Sopenharmony_ci			netif_stop_queue(dev->net);
393562306a36Sopenharmony_ci			spin_unlock_irqrestore(&dev->txq.lock, flags);
393662306a36Sopenharmony_ci			netdev_dbg(dev->net,
393762306a36Sopenharmony_ci				   "Delaying transmission for resumption\n");
393862306a36Sopenharmony_ci			return;
393962306a36Sopenharmony_ci		}
394062306a36Sopenharmony_ci#endif
394162306a36Sopenharmony_ci		ret = usb_submit_urb(entry->urb, GFP_ATOMIC);
394262306a36Sopenharmony_ci		switch (ret) {
394362306a36Sopenharmony_ci		case 0:
394462306a36Sopenharmony_ci			netif_trans_update(dev->net);
394562306a36Sopenharmony_ci			lan78xx_queue_skb(&dev->txq, tx_buf, tx_start);
394662306a36Sopenharmony_ci			break;
394762306a36Sopenharmony_ci		case -EPIPE:
394862306a36Sopenharmony_ci			netif_stop_queue(dev->net);
394962306a36Sopenharmony_ci			lan78xx_defer_kevent(dev, EVENT_TX_HALT);
395062306a36Sopenharmony_ci			usb_autopm_put_interface_async(dev->intf);
395162306a36Sopenharmony_ci			break;
395262306a36Sopenharmony_ci		case -ENODEV:
395362306a36Sopenharmony_ci		case -ENOENT:
395462306a36Sopenharmony_ci			netif_dbg(dev, tx_err, dev->net,
395562306a36Sopenharmony_ci				  "tx submit urb err %d (disconnected?)", ret);
395662306a36Sopenharmony_ci			netif_device_detach(dev->net);
395762306a36Sopenharmony_ci			break;
395862306a36Sopenharmony_ci		default:
395962306a36Sopenharmony_ci			usb_autopm_put_interface_async(dev->intf);
396062306a36Sopenharmony_ci			netif_dbg(dev, tx_err, dev->net,
396162306a36Sopenharmony_ci				  "tx submit urb err %d\n", ret);
396262306a36Sopenharmony_ci			break;
396362306a36Sopenharmony_ci		}
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->txq.lock, flags);
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci		if (ret) {
396862306a36Sopenharmony_ci			netdev_warn(dev->net, "failed to tx urb %d\n", ret);
396962306a36Sopenharmony_ciout:
397062306a36Sopenharmony_ci			dev->net->stats.tx_dropped += entry->num_of_packet;
397162306a36Sopenharmony_ci			lan78xx_release_tx_buf(dev, tx_buf);
397262306a36Sopenharmony_ci		}
397362306a36Sopenharmony_ci	} while (ret == 0);
397462306a36Sopenharmony_ci}
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_cistatic int lan78xx_bh(struct lan78xx_net *dev, int budget)
397762306a36Sopenharmony_ci{
397862306a36Sopenharmony_ci	struct sk_buff_head done;
397962306a36Sopenharmony_ci	struct sk_buff *rx_buf;
398062306a36Sopenharmony_ci	struct skb_data *entry;
398162306a36Sopenharmony_ci	unsigned long flags;
398262306a36Sopenharmony_ci	int work_done = 0;
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	/* Pass frames received in the last NAPI cycle before
398562306a36Sopenharmony_ci	 * working on newly completed URBs.
398662306a36Sopenharmony_ci	 */
398762306a36Sopenharmony_ci	while (!skb_queue_empty(&dev->rxq_overflow)) {
398862306a36Sopenharmony_ci		lan78xx_skb_return(dev, skb_dequeue(&dev->rxq_overflow));
398962306a36Sopenharmony_ci		++work_done;
399062306a36Sopenharmony_ci	}
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	/* Take a snapshot of the done queue and move items to a
399362306a36Sopenharmony_ci	 * temporary queue. Rx URB completions will continue to add
399462306a36Sopenharmony_ci	 * to the done queue.
399562306a36Sopenharmony_ci	 */
399662306a36Sopenharmony_ci	__skb_queue_head_init(&done);
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rxq_done.lock, flags);
399962306a36Sopenharmony_ci	skb_queue_splice_init(&dev->rxq_done, &done);
400062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rxq_done.lock, flags);
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci	/* Extract receive frames from completed URBs and
400362306a36Sopenharmony_ci	 * pass them to the stack. Re-submit each completed URB.
400462306a36Sopenharmony_ci	 */
400562306a36Sopenharmony_ci	while ((work_done < budget) &&
400662306a36Sopenharmony_ci	       (rx_buf = __skb_dequeue(&done))) {
400762306a36Sopenharmony_ci		entry = (struct skb_data *)(rx_buf->cb);
400862306a36Sopenharmony_ci		switch (entry->state) {
400962306a36Sopenharmony_ci		case rx_done:
401062306a36Sopenharmony_ci			rx_process(dev, rx_buf, budget, &work_done);
401162306a36Sopenharmony_ci			break;
401262306a36Sopenharmony_ci		case rx_cleanup:
401362306a36Sopenharmony_ci			break;
401462306a36Sopenharmony_ci		default:
401562306a36Sopenharmony_ci			netdev_dbg(dev->net, "rx buf state %d\n",
401662306a36Sopenharmony_ci				   entry->state);
401762306a36Sopenharmony_ci			break;
401862306a36Sopenharmony_ci		}
401962306a36Sopenharmony_ci
402062306a36Sopenharmony_ci		lan78xx_rx_urb_resubmit(dev, rx_buf);
402162306a36Sopenharmony_ci	}
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci	/* If budget was consumed before processing all the URBs put them
402462306a36Sopenharmony_ci	 * back on the front of the done queue. They will be first to be
402562306a36Sopenharmony_ci	 * processed in the next NAPI cycle.
402662306a36Sopenharmony_ci	 */
402762306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rxq_done.lock, flags);
402862306a36Sopenharmony_ci	skb_queue_splice(&done, &dev->rxq_done);
402962306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rxq_done.lock, flags);
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_ci	if (netif_device_present(dev->net) && netif_running(dev->net)) {
403262306a36Sopenharmony_ci		/* reset update timer delta */
403362306a36Sopenharmony_ci		if (timer_pending(&dev->stat_monitor) && (dev->delta != 1)) {
403462306a36Sopenharmony_ci			dev->delta = 1;
403562306a36Sopenharmony_ci			mod_timer(&dev->stat_monitor,
403662306a36Sopenharmony_ci				  jiffies + STAT_UPDATE_TIMER);
403762306a36Sopenharmony_ci		}
403862306a36Sopenharmony_ci
403962306a36Sopenharmony_ci		/* Submit all free Rx URBs */
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci		if (!test_bit(EVENT_RX_HALT, &dev->flags))
404262306a36Sopenharmony_ci			lan78xx_rx_urb_submit_all(dev);
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_ci		/* Submit new Tx URBs */
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci		lan78xx_tx_bh(dev);
404762306a36Sopenharmony_ci	}
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	return work_done;
405062306a36Sopenharmony_ci}
405162306a36Sopenharmony_ci
405262306a36Sopenharmony_cistatic int lan78xx_poll(struct napi_struct *napi, int budget)
405362306a36Sopenharmony_ci{
405462306a36Sopenharmony_ci	struct lan78xx_net *dev = container_of(napi, struct lan78xx_net, napi);
405562306a36Sopenharmony_ci	int result = budget;
405662306a36Sopenharmony_ci	int work_done;
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_ci	/* Don't do any work if the device is suspended */
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci	if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
406162306a36Sopenharmony_ci		napi_complete_done(napi, 0);
406262306a36Sopenharmony_ci		return 0;
406362306a36Sopenharmony_ci	}
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_ci	/* Process completed URBs and submit new URBs */
406662306a36Sopenharmony_ci
406762306a36Sopenharmony_ci	work_done = lan78xx_bh(dev, budget);
406862306a36Sopenharmony_ci
406962306a36Sopenharmony_ci	if (work_done < budget) {
407062306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_ci		/* Start a new polling cycle if data was received or
407362306a36Sopenharmony_ci		 * data is waiting to be transmitted.
407462306a36Sopenharmony_ci		 */
407562306a36Sopenharmony_ci		if (!skb_queue_empty(&dev->rxq_done)) {
407662306a36Sopenharmony_ci			napi_schedule(napi);
407762306a36Sopenharmony_ci		} else if (netif_carrier_ok(dev->net)) {
407862306a36Sopenharmony_ci			if (skb_queue_empty(&dev->txq) &&
407962306a36Sopenharmony_ci			    !skb_queue_empty(&dev->txq_pend)) {
408062306a36Sopenharmony_ci				napi_schedule(napi);
408162306a36Sopenharmony_ci			} else {
408262306a36Sopenharmony_ci				netif_tx_lock(dev->net);
408362306a36Sopenharmony_ci				if (netif_queue_stopped(dev->net)) {
408462306a36Sopenharmony_ci					netif_wake_queue(dev->net);
408562306a36Sopenharmony_ci					napi_schedule(napi);
408662306a36Sopenharmony_ci				}
408762306a36Sopenharmony_ci				netif_tx_unlock(dev->net);
408862306a36Sopenharmony_ci			}
408962306a36Sopenharmony_ci		}
409062306a36Sopenharmony_ci		result = work_done;
409162306a36Sopenharmony_ci	}
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci	return result;
409462306a36Sopenharmony_ci}
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_cistatic void lan78xx_delayedwork(struct work_struct *work)
409762306a36Sopenharmony_ci{
409862306a36Sopenharmony_ci	int status;
409962306a36Sopenharmony_ci	struct lan78xx_net *dev;
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci	dev = container_of(work, struct lan78xx_net, wq.work);
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags))
410462306a36Sopenharmony_ci		return;
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci	if (usb_autopm_get_interface(dev->intf) < 0)
410762306a36Sopenharmony_ci		return;
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci	if (test_bit(EVENT_TX_HALT, &dev->flags)) {
411062306a36Sopenharmony_ci		unlink_urbs(dev, &dev->txq);
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci		status = usb_clear_halt(dev->udev, dev->pipe_out);
411362306a36Sopenharmony_ci		if (status < 0 &&
411462306a36Sopenharmony_ci		    status != -EPIPE &&
411562306a36Sopenharmony_ci		    status != -ESHUTDOWN) {
411662306a36Sopenharmony_ci			if (netif_msg_tx_err(dev))
411762306a36Sopenharmony_ci				netdev_err(dev->net,
411862306a36Sopenharmony_ci					   "can't clear tx halt, status %d\n",
411962306a36Sopenharmony_ci					   status);
412062306a36Sopenharmony_ci		} else {
412162306a36Sopenharmony_ci			clear_bit(EVENT_TX_HALT, &dev->flags);
412262306a36Sopenharmony_ci			if (status != -ESHUTDOWN)
412362306a36Sopenharmony_ci				netif_wake_queue(dev->net);
412462306a36Sopenharmony_ci		}
412562306a36Sopenharmony_ci	}
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci	if (test_bit(EVENT_RX_HALT, &dev->flags)) {
412862306a36Sopenharmony_ci		unlink_urbs(dev, &dev->rxq);
412962306a36Sopenharmony_ci		status = usb_clear_halt(dev->udev, dev->pipe_in);
413062306a36Sopenharmony_ci		if (status < 0 &&
413162306a36Sopenharmony_ci		    status != -EPIPE &&
413262306a36Sopenharmony_ci		    status != -ESHUTDOWN) {
413362306a36Sopenharmony_ci			if (netif_msg_rx_err(dev))
413462306a36Sopenharmony_ci				netdev_err(dev->net,
413562306a36Sopenharmony_ci					   "can't clear rx halt, status %d\n",
413662306a36Sopenharmony_ci					   status);
413762306a36Sopenharmony_ci		} else {
413862306a36Sopenharmony_ci			clear_bit(EVENT_RX_HALT, &dev->flags);
413962306a36Sopenharmony_ci			napi_schedule(&dev->napi);
414062306a36Sopenharmony_ci		}
414162306a36Sopenharmony_ci	}
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	if (test_bit(EVENT_LINK_RESET, &dev->flags)) {
414462306a36Sopenharmony_ci		int ret = 0;
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci		clear_bit(EVENT_LINK_RESET, &dev->flags);
414762306a36Sopenharmony_ci		if (lan78xx_link_reset(dev) < 0) {
414862306a36Sopenharmony_ci			netdev_info(dev->net, "link reset failed (%d)\n",
414962306a36Sopenharmony_ci				    ret);
415062306a36Sopenharmony_ci		}
415162306a36Sopenharmony_ci	}
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	if (test_bit(EVENT_STAT_UPDATE, &dev->flags)) {
415462306a36Sopenharmony_ci		lan78xx_update_stats(dev);
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci		clear_bit(EVENT_STAT_UPDATE, &dev->flags);
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci		mod_timer(&dev->stat_monitor,
415962306a36Sopenharmony_ci			  jiffies + (STAT_UPDATE_TIMER * dev->delta));
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci		dev->delta = min((dev->delta * 2), 50);
416262306a36Sopenharmony_ci	}
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	usb_autopm_put_interface(dev->intf);
416562306a36Sopenharmony_ci}
416662306a36Sopenharmony_ci
416762306a36Sopenharmony_cistatic void intr_complete(struct urb *urb)
416862306a36Sopenharmony_ci{
416962306a36Sopenharmony_ci	struct lan78xx_net *dev = urb->context;
417062306a36Sopenharmony_ci	int status = urb->status;
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	switch (status) {
417362306a36Sopenharmony_ci	/* success */
417462306a36Sopenharmony_ci	case 0:
417562306a36Sopenharmony_ci		lan78xx_status(dev, urb);
417662306a36Sopenharmony_ci		break;
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_ci	/* software-driven interface shutdown */
417962306a36Sopenharmony_ci	case -ENOENT:			/* urb killed */
418062306a36Sopenharmony_ci	case -ENODEV:			/* hardware gone */
418162306a36Sopenharmony_ci	case -ESHUTDOWN:		/* hardware gone */
418262306a36Sopenharmony_ci		netif_dbg(dev, ifdown, dev->net,
418362306a36Sopenharmony_ci			  "intr shutdown, code %d\n", status);
418462306a36Sopenharmony_ci		return;
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	/* NOTE:  not throttling like RX/TX, since this endpoint
418762306a36Sopenharmony_ci	 * already polls infrequently
418862306a36Sopenharmony_ci	 */
418962306a36Sopenharmony_ci	default:
419062306a36Sopenharmony_ci		netdev_dbg(dev->net, "intr status %d\n", status);
419162306a36Sopenharmony_ci		break;
419262306a36Sopenharmony_ci	}
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci	if (!netif_device_present(dev->net) ||
419562306a36Sopenharmony_ci	    !netif_running(dev->net)) {
419662306a36Sopenharmony_ci		netdev_warn(dev->net, "not submitting new status URB");
419762306a36Sopenharmony_ci		return;
419862306a36Sopenharmony_ci	}
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_ci	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
420162306a36Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	switch (status) {
420462306a36Sopenharmony_ci	case  0:
420562306a36Sopenharmony_ci		break;
420662306a36Sopenharmony_ci	case -ENODEV:
420762306a36Sopenharmony_ci	case -ENOENT:
420862306a36Sopenharmony_ci		netif_dbg(dev, timer, dev->net,
420962306a36Sopenharmony_ci			  "intr resubmit %d (disconnect?)", status);
421062306a36Sopenharmony_ci		netif_device_detach(dev->net);
421162306a36Sopenharmony_ci		break;
421262306a36Sopenharmony_ci	default:
421362306a36Sopenharmony_ci		netif_err(dev, timer, dev->net,
421462306a36Sopenharmony_ci			  "intr resubmit --> %d\n", status);
421562306a36Sopenharmony_ci		break;
421662306a36Sopenharmony_ci	}
421762306a36Sopenharmony_ci}
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_cistatic void lan78xx_disconnect(struct usb_interface *intf)
422062306a36Sopenharmony_ci{
422162306a36Sopenharmony_ci	struct lan78xx_net *dev;
422262306a36Sopenharmony_ci	struct usb_device *udev;
422362306a36Sopenharmony_ci	struct net_device *net;
422462306a36Sopenharmony_ci	struct phy_device *phydev;
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	dev = usb_get_intfdata(intf);
422762306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
422862306a36Sopenharmony_ci	if (!dev)
422962306a36Sopenharmony_ci		return;
423062306a36Sopenharmony_ci
423162306a36Sopenharmony_ci	netif_napi_del(&dev->napi);
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci	udev = interface_to_usbdev(intf);
423462306a36Sopenharmony_ci	net = dev->net;
423562306a36Sopenharmony_ci
423662306a36Sopenharmony_ci	unregister_netdev(net);
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci	timer_shutdown_sync(&dev->stat_monitor);
423962306a36Sopenharmony_ci	set_bit(EVENT_DEV_DISCONNECT, &dev->flags);
424062306a36Sopenharmony_ci	cancel_delayed_work_sync(&dev->wq);
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci	phydev = net->phydev;
424362306a36Sopenharmony_ci
424462306a36Sopenharmony_ci	phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0);
424562306a36Sopenharmony_ci	phy_unregister_fixup_for_uid(PHY_LAN8835, 0xfffffff0);
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_ci	phy_disconnect(net->phydev);
424862306a36Sopenharmony_ci
424962306a36Sopenharmony_ci	if (phy_is_pseudo_fixed_link(phydev))
425062306a36Sopenharmony_ci		fixed_phy_unregister(phydev);
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci	usb_scuttle_anchored_urbs(&dev->deferred);
425362306a36Sopenharmony_ci
425462306a36Sopenharmony_ci	lan78xx_unbind(dev, intf);
425562306a36Sopenharmony_ci
425662306a36Sopenharmony_ci	lan78xx_free_tx_resources(dev);
425762306a36Sopenharmony_ci	lan78xx_free_rx_resources(dev);
425862306a36Sopenharmony_ci
425962306a36Sopenharmony_ci	usb_kill_urb(dev->urb_intr);
426062306a36Sopenharmony_ci	usb_free_urb(dev->urb_intr);
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	free_netdev(net);
426362306a36Sopenharmony_ci	usb_put_dev(udev);
426462306a36Sopenharmony_ci}
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_cistatic void lan78xx_tx_timeout(struct net_device *net, unsigned int txqueue)
426762306a36Sopenharmony_ci{
426862306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(net);
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci	unlink_urbs(dev, &dev->txq);
427162306a36Sopenharmony_ci	napi_schedule(&dev->napi);
427262306a36Sopenharmony_ci}
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_cistatic netdev_features_t lan78xx_features_check(struct sk_buff *skb,
427562306a36Sopenharmony_ci						struct net_device *netdev,
427662306a36Sopenharmony_ci						netdev_features_t features)
427762306a36Sopenharmony_ci{
427862306a36Sopenharmony_ci	struct lan78xx_net *dev = netdev_priv(netdev);
427962306a36Sopenharmony_ci
428062306a36Sopenharmony_ci	if (skb->len > LAN78XX_TSO_SIZE(dev))
428162306a36Sopenharmony_ci		features &= ~NETIF_F_GSO_MASK;
428262306a36Sopenharmony_ci
428362306a36Sopenharmony_ci	features = vlan_features_check(skb, features);
428462306a36Sopenharmony_ci	features = vxlan_features_check(skb, features);
428562306a36Sopenharmony_ci
428662306a36Sopenharmony_ci	return features;
428762306a36Sopenharmony_ci}
428862306a36Sopenharmony_ci
428962306a36Sopenharmony_cistatic const struct net_device_ops lan78xx_netdev_ops = {
429062306a36Sopenharmony_ci	.ndo_open		= lan78xx_open,
429162306a36Sopenharmony_ci	.ndo_stop		= lan78xx_stop,
429262306a36Sopenharmony_ci	.ndo_start_xmit		= lan78xx_start_xmit,
429362306a36Sopenharmony_ci	.ndo_tx_timeout		= lan78xx_tx_timeout,
429462306a36Sopenharmony_ci	.ndo_change_mtu		= lan78xx_change_mtu,
429562306a36Sopenharmony_ci	.ndo_set_mac_address	= lan78xx_set_mac_addr,
429662306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
429762306a36Sopenharmony_ci	.ndo_eth_ioctl		= phy_do_ioctl_running,
429862306a36Sopenharmony_ci	.ndo_set_rx_mode	= lan78xx_set_multicast,
429962306a36Sopenharmony_ci	.ndo_set_features	= lan78xx_set_features,
430062306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= lan78xx_vlan_rx_add_vid,
430162306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= lan78xx_vlan_rx_kill_vid,
430262306a36Sopenharmony_ci	.ndo_features_check	= lan78xx_features_check,
430362306a36Sopenharmony_ci};
430462306a36Sopenharmony_ci
430562306a36Sopenharmony_cistatic void lan78xx_stat_monitor(struct timer_list *t)
430662306a36Sopenharmony_ci{
430762306a36Sopenharmony_ci	struct lan78xx_net *dev = from_timer(dev, t, stat_monitor);
430862306a36Sopenharmony_ci
430962306a36Sopenharmony_ci	lan78xx_defer_kevent(dev, EVENT_STAT_UPDATE);
431062306a36Sopenharmony_ci}
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_cistatic int lan78xx_probe(struct usb_interface *intf,
431362306a36Sopenharmony_ci			 const struct usb_device_id *id)
431462306a36Sopenharmony_ci{
431562306a36Sopenharmony_ci	struct usb_host_endpoint *ep_blkin, *ep_blkout, *ep_intr;
431662306a36Sopenharmony_ci	struct lan78xx_net *dev;
431762306a36Sopenharmony_ci	struct net_device *netdev;
431862306a36Sopenharmony_ci	struct usb_device *udev;
431962306a36Sopenharmony_ci	int ret;
432062306a36Sopenharmony_ci	unsigned int maxp;
432162306a36Sopenharmony_ci	unsigned int period;
432262306a36Sopenharmony_ci	u8 *buf = NULL;
432362306a36Sopenharmony_ci
432462306a36Sopenharmony_ci	udev = interface_to_usbdev(intf);
432562306a36Sopenharmony_ci	udev = usb_get_dev(udev);
432662306a36Sopenharmony_ci
432762306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct lan78xx_net));
432862306a36Sopenharmony_ci	if (!netdev) {
432962306a36Sopenharmony_ci		dev_err(&intf->dev, "Error: OOM\n");
433062306a36Sopenharmony_ci		ret = -ENOMEM;
433162306a36Sopenharmony_ci		goto out1;
433262306a36Sopenharmony_ci	}
433362306a36Sopenharmony_ci
433462306a36Sopenharmony_ci	/* netdev_printk() needs this */
433562306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &intf->dev);
433662306a36Sopenharmony_ci
433762306a36Sopenharmony_ci	dev = netdev_priv(netdev);
433862306a36Sopenharmony_ci	dev->udev = udev;
433962306a36Sopenharmony_ci	dev->intf = intf;
434062306a36Sopenharmony_ci	dev->net = netdev;
434162306a36Sopenharmony_ci	dev->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV
434262306a36Sopenharmony_ci					| NETIF_MSG_PROBE | NETIF_MSG_LINK);
434362306a36Sopenharmony_ci
434462306a36Sopenharmony_ci	skb_queue_head_init(&dev->rxq);
434562306a36Sopenharmony_ci	skb_queue_head_init(&dev->txq);
434662306a36Sopenharmony_ci	skb_queue_head_init(&dev->rxq_done);
434762306a36Sopenharmony_ci	skb_queue_head_init(&dev->txq_pend);
434862306a36Sopenharmony_ci	skb_queue_head_init(&dev->rxq_overflow);
434962306a36Sopenharmony_ci	mutex_init(&dev->phy_mutex);
435062306a36Sopenharmony_ci	mutex_init(&dev->dev_mutex);
435162306a36Sopenharmony_ci
435262306a36Sopenharmony_ci	ret = lan78xx_urb_config_init(dev);
435362306a36Sopenharmony_ci	if (ret < 0)
435462306a36Sopenharmony_ci		goto out2;
435562306a36Sopenharmony_ci
435662306a36Sopenharmony_ci	ret = lan78xx_alloc_tx_resources(dev);
435762306a36Sopenharmony_ci	if (ret < 0)
435862306a36Sopenharmony_ci		goto out2;
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	ret = lan78xx_alloc_rx_resources(dev);
436162306a36Sopenharmony_ci	if (ret < 0)
436262306a36Sopenharmony_ci		goto out3;
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	/* MTU range: 68 - 9000 */
436562306a36Sopenharmony_ci	netdev->max_mtu = MAX_SINGLE_PACKET_SIZE;
436662306a36Sopenharmony_ci
436762306a36Sopenharmony_ci	netif_set_tso_max_size(netdev, LAN78XX_TSO_SIZE(dev));
436862306a36Sopenharmony_ci
436962306a36Sopenharmony_ci	netif_napi_add(netdev, &dev->napi, lan78xx_poll);
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&dev->wq, lan78xx_delayedwork);
437262306a36Sopenharmony_ci	init_usb_anchor(&dev->deferred);
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci	netdev->netdev_ops = &lan78xx_netdev_ops;
437562306a36Sopenharmony_ci	netdev->watchdog_timeo = TX_TIMEOUT_JIFFIES;
437662306a36Sopenharmony_ci	netdev->ethtool_ops = &lan78xx_ethtool_ops;
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci	dev->delta = 1;
437962306a36Sopenharmony_ci	timer_setup(&dev->stat_monitor, lan78xx_stat_monitor, 0);
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_ci	mutex_init(&dev->stats.access_lock);
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bNumEndpoints < 3) {
438462306a36Sopenharmony_ci		ret = -ENODEV;
438562306a36Sopenharmony_ci		goto out4;
438662306a36Sopenharmony_ci	}
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	dev->pipe_in = usb_rcvbulkpipe(udev, BULK_IN_PIPE);
438962306a36Sopenharmony_ci	ep_blkin = usb_pipe_endpoint(udev, dev->pipe_in);
439062306a36Sopenharmony_ci	if (!ep_blkin || !usb_endpoint_is_bulk_in(&ep_blkin->desc)) {
439162306a36Sopenharmony_ci		ret = -ENODEV;
439262306a36Sopenharmony_ci		goto out4;
439362306a36Sopenharmony_ci	}
439462306a36Sopenharmony_ci
439562306a36Sopenharmony_ci	dev->pipe_out = usb_sndbulkpipe(udev, BULK_OUT_PIPE);
439662306a36Sopenharmony_ci	ep_blkout = usb_pipe_endpoint(udev, dev->pipe_out);
439762306a36Sopenharmony_ci	if (!ep_blkout || !usb_endpoint_is_bulk_out(&ep_blkout->desc)) {
439862306a36Sopenharmony_ci		ret = -ENODEV;
439962306a36Sopenharmony_ci		goto out4;
440062306a36Sopenharmony_ci	}
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci	ep_intr = &intf->cur_altsetting->endpoint[2];
440362306a36Sopenharmony_ci	if (!usb_endpoint_is_int_in(&ep_intr->desc)) {
440462306a36Sopenharmony_ci		ret = -ENODEV;
440562306a36Sopenharmony_ci		goto out4;
440662306a36Sopenharmony_ci	}
440762306a36Sopenharmony_ci
440862306a36Sopenharmony_ci	dev->pipe_intr = usb_rcvintpipe(dev->udev,
440962306a36Sopenharmony_ci					usb_endpoint_num(&ep_intr->desc));
441062306a36Sopenharmony_ci
441162306a36Sopenharmony_ci	ret = lan78xx_bind(dev, intf);
441262306a36Sopenharmony_ci	if (ret < 0)
441362306a36Sopenharmony_ci		goto out4;
441462306a36Sopenharmony_ci
441562306a36Sopenharmony_ci	period = ep_intr->desc.bInterval;
441662306a36Sopenharmony_ci	maxp = usb_maxpacket(dev->udev, dev->pipe_intr);
441762306a36Sopenharmony_ci	buf = kmalloc(maxp, GFP_KERNEL);
441862306a36Sopenharmony_ci	if (!buf) {
441962306a36Sopenharmony_ci		ret = -ENOMEM;
442062306a36Sopenharmony_ci		goto out5;
442162306a36Sopenharmony_ci	}
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci	dev->urb_intr = usb_alloc_urb(0, GFP_KERNEL);
442462306a36Sopenharmony_ci	if (!dev->urb_intr) {
442562306a36Sopenharmony_ci		ret = -ENOMEM;
442662306a36Sopenharmony_ci		goto out6;
442762306a36Sopenharmony_ci	} else {
442862306a36Sopenharmony_ci		usb_fill_int_urb(dev->urb_intr, dev->udev,
442962306a36Sopenharmony_ci				 dev->pipe_intr, buf, maxp,
443062306a36Sopenharmony_ci				 intr_complete, dev, period);
443162306a36Sopenharmony_ci		dev->urb_intr->transfer_flags |= URB_FREE_BUFFER;
443262306a36Sopenharmony_ci	}
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_ci	dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out);
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci	/* Reject broken descriptors. */
443762306a36Sopenharmony_ci	if (dev->maxpacket == 0) {
443862306a36Sopenharmony_ci		ret = -ENODEV;
443962306a36Sopenharmony_ci		goto out6;
444062306a36Sopenharmony_ci	}
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_ci	/* driver requires remote-wakeup capability during autosuspend. */
444362306a36Sopenharmony_ci	intf->needs_remote_wakeup = 1;
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci	ret = lan78xx_phy_init(dev);
444662306a36Sopenharmony_ci	if (ret < 0)
444762306a36Sopenharmony_ci		goto out7;
444862306a36Sopenharmony_ci
444962306a36Sopenharmony_ci	ret = register_netdev(netdev);
445062306a36Sopenharmony_ci	if (ret != 0) {
445162306a36Sopenharmony_ci		netif_err(dev, probe, netdev, "couldn't register the device\n");
445262306a36Sopenharmony_ci		goto out8;
445362306a36Sopenharmony_ci	}
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	usb_set_intfdata(intf, dev);
445662306a36Sopenharmony_ci
445762306a36Sopenharmony_ci	ret = device_set_wakeup_enable(&udev->dev, true);
445862306a36Sopenharmony_ci
445962306a36Sopenharmony_ci	 /* Default delay of 2sec has more overhead than advantage.
446062306a36Sopenharmony_ci	  * Set to 10sec as default.
446162306a36Sopenharmony_ci	  */
446262306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&udev->dev,
446362306a36Sopenharmony_ci					 DEFAULT_AUTOSUSPEND_DELAY);
446462306a36Sopenharmony_ci
446562306a36Sopenharmony_ci	return 0;
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ciout8:
446862306a36Sopenharmony_ci	phy_disconnect(netdev->phydev);
446962306a36Sopenharmony_ciout7:
447062306a36Sopenharmony_ci	usb_free_urb(dev->urb_intr);
447162306a36Sopenharmony_ciout6:
447262306a36Sopenharmony_ci	kfree(buf);
447362306a36Sopenharmony_ciout5:
447462306a36Sopenharmony_ci	lan78xx_unbind(dev, intf);
447562306a36Sopenharmony_ciout4:
447662306a36Sopenharmony_ci	netif_napi_del(&dev->napi);
447762306a36Sopenharmony_ci	lan78xx_free_rx_resources(dev);
447862306a36Sopenharmony_ciout3:
447962306a36Sopenharmony_ci	lan78xx_free_tx_resources(dev);
448062306a36Sopenharmony_ciout2:
448162306a36Sopenharmony_ci	free_netdev(netdev);
448262306a36Sopenharmony_ciout1:
448362306a36Sopenharmony_ci	usb_put_dev(udev);
448462306a36Sopenharmony_ci
448562306a36Sopenharmony_ci	return ret;
448662306a36Sopenharmony_ci}
448762306a36Sopenharmony_ci
448862306a36Sopenharmony_cistatic u16 lan78xx_wakeframe_crc16(const u8 *buf, int len)
448962306a36Sopenharmony_ci{
449062306a36Sopenharmony_ci	const u16 crc16poly = 0x8005;
449162306a36Sopenharmony_ci	int i;
449262306a36Sopenharmony_ci	u16 bit, crc, msb;
449362306a36Sopenharmony_ci	u8 data;
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	crc = 0xFFFF;
449662306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
449762306a36Sopenharmony_ci		data = *buf++;
449862306a36Sopenharmony_ci		for (bit = 0; bit < 8; bit++) {
449962306a36Sopenharmony_ci			msb = crc >> 15;
450062306a36Sopenharmony_ci			crc <<= 1;
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_ci			if (msb ^ (u16)(data & 1)) {
450362306a36Sopenharmony_ci				crc ^= crc16poly;
450462306a36Sopenharmony_ci				crc |= (u16)0x0001U;
450562306a36Sopenharmony_ci			}
450662306a36Sopenharmony_ci			data >>= 1;
450762306a36Sopenharmony_ci		}
450862306a36Sopenharmony_ci	}
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_ci	return crc;
451162306a36Sopenharmony_ci}
451262306a36Sopenharmony_ci
451362306a36Sopenharmony_cistatic int lan78xx_set_auto_suspend(struct lan78xx_net *dev)
451462306a36Sopenharmony_ci{
451562306a36Sopenharmony_ci	u32 buf;
451662306a36Sopenharmony_ci	int ret;
451762306a36Sopenharmony_ci
451862306a36Sopenharmony_ci	ret = lan78xx_stop_tx_path(dev);
451962306a36Sopenharmony_ci	if (ret < 0)
452062306a36Sopenharmony_ci		return ret;
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_ci	ret = lan78xx_stop_rx_path(dev);
452362306a36Sopenharmony_ci	if (ret < 0)
452462306a36Sopenharmony_ci		return ret;
452562306a36Sopenharmony_ci
452662306a36Sopenharmony_ci	/* auto suspend (selective suspend) */
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR, 0);
452962306a36Sopenharmony_ci	if (ret < 0)
453062306a36Sopenharmony_ci		return ret;
453162306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR2, 0);
453262306a36Sopenharmony_ci	if (ret < 0)
453362306a36Sopenharmony_ci		return ret;
453462306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL);
453562306a36Sopenharmony_ci	if (ret < 0)
453662306a36Sopenharmony_ci		return ret;
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci	/* set goodframe wakeup */
453962306a36Sopenharmony_ci
454062306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, WUCSR, &buf);
454162306a36Sopenharmony_ci	if (ret < 0)
454262306a36Sopenharmony_ci		return ret;
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci	buf |= WUCSR_RFE_WAKE_EN_;
454562306a36Sopenharmony_ci	buf |= WUCSR_STORE_WAKE_;
454662306a36Sopenharmony_ci
454762306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR, buf);
454862306a36Sopenharmony_ci	if (ret < 0)
454962306a36Sopenharmony_ci		return ret;
455062306a36Sopenharmony_ci
455162306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
455262306a36Sopenharmony_ci	if (ret < 0)
455362306a36Sopenharmony_ci		return ret;
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci	buf &= ~PMT_CTL_RES_CLR_WKP_EN_;
455662306a36Sopenharmony_ci	buf |= PMT_CTL_RES_CLR_WKP_STS_;
455762306a36Sopenharmony_ci	buf |= PMT_CTL_PHY_WAKE_EN_;
455862306a36Sopenharmony_ci	buf |= PMT_CTL_WOL_EN_;
455962306a36Sopenharmony_ci	buf &= ~PMT_CTL_SUS_MODE_MASK_;
456062306a36Sopenharmony_ci	buf |= PMT_CTL_SUS_MODE_3_;
456162306a36Sopenharmony_ci
456262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, PMT_CTL, buf);
456362306a36Sopenharmony_ci	if (ret < 0)
456462306a36Sopenharmony_ci		return ret;
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
456762306a36Sopenharmony_ci	if (ret < 0)
456862306a36Sopenharmony_ci		return ret;
456962306a36Sopenharmony_ci
457062306a36Sopenharmony_ci	buf |= PMT_CTL_WUPS_MASK_;
457162306a36Sopenharmony_ci
457262306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, PMT_CTL, buf);
457362306a36Sopenharmony_ci	if (ret < 0)
457462306a36Sopenharmony_ci		return ret;
457562306a36Sopenharmony_ci
457662306a36Sopenharmony_ci	ret = lan78xx_start_rx_path(dev);
457762306a36Sopenharmony_ci
457862306a36Sopenharmony_ci	return ret;
457962306a36Sopenharmony_ci}
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_cistatic int lan78xx_set_suspend(struct lan78xx_net *dev, u32 wol)
458262306a36Sopenharmony_ci{
458362306a36Sopenharmony_ci	const u8 ipv4_multicast[3] = { 0x01, 0x00, 0x5E };
458462306a36Sopenharmony_ci	const u8 ipv6_multicast[3] = { 0x33, 0x33 };
458562306a36Sopenharmony_ci	const u8 arp_type[2] = { 0x08, 0x06 };
458662306a36Sopenharmony_ci	u32 temp_pmt_ctl;
458762306a36Sopenharmony_ci	int mask_index;
458862306a36Sopenharmony_ci	u32 temp_wucsr;
458962306a36Sopenharmony_ci	u32 buf;
459062306a36Sopenharmony_ci	u16 crc;
459162306a36Sopenharmony_ci	int ret;
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci	ret = lan78xx_stop_tx_path(dev);
459462306a36Sopenharmony_ci	if (ret < 0)
459562306a36Sopenharmony_ci		return ret;
459662306a36Sopenharmony_ci	ret = lan78xx_stop_rx_path(dev);
459762306a36Sopenharmony_ci	if (ret < 0)
459862306a36Sopenharmony_ci		return ret;
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR, 0);
460162306a36Sopenharmony_ci	if (ret < 0)
460262306a36Sopenharmony_ci		return ret;
460362306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR2, 0);
460462306a36Sopenharmony_ci	if (ret < 0)
460562306a36Sopenharmony_ci		return ret;
460662306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL);
460762306a36Sopenharmony_ci	if (ret < 0)
460862306a36Sopenharmony_ci		return ret;
460962306a36Sopenharmony_ci
461062306a36Sopenharmony_ci	temp_wucsr = 0;
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci	temp_pmt_ctl = 0;
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, PMT_CTL, &temp_pmt_ctl);
461562306a36Sopenharmony_ci	if (ret < 0)
461662306a36Sopenharmony_ci		return ret;
461762306a36Sopenharmony_ci
461862306a36Sopenharmony_ci	temp_pmt_ctl &= ~PMT_CTL_RES_CLR_WKP_EN_;
461962306a36Sopenharmony_ci	temp_pmt_ctl |= PMT_CTL_RES_CLR_WKP_STS_;
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci	for (mask_index = 0; mask_index < NUM_OF_WUF_CFG; mask_index++) {
462262306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_CFG(mask_index), 0);
462362306a36Sopenharmony_ci		if (ret < 0)
462462306a36Sopenharmony_ci			return ret;
462562306a36Sopenharmony_ci	}
462662306a36Sopenharmony_ci
462762306a36Sopenharmony_ci	mask_index = 0;
462862306a36Sopenharmony_ci	if (wol & WAKE_PHY) {
462962306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_PHY_WAKE_EN_;
463062306a36Sopenharmony_ci
463162306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
463262306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
463362306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_;
463462306a36Sopenharmony_ci	}
463562306a36Sopenharmony_ci	if (wol & WAKE_MAGIC) {
463662306a36Sopenharmony_ci		temp_wucsr |= WUCSR_MPEN_;
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
463962306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
464062306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_3_;
464162306a36Sopenharmony_ci	}
464262306a36Sopenharmony_ci	if (wol & WAKE_BCAST) {
464362306a36Sopenharmony_ci		temp_wucsr |= WUCSR_BCST_EN_;
464462306a36Sopenharmony_ci
464562306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
464662306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
464762306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_;
464862306a36Sopenharmony_ci	}
464962306a36Sopenharmony_ci	if (wol & WAKE_MCAST) {
465062306a36Sopenharmony_ci		temp_wucsr |= WUCSR_WAKE_EN_;
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci		/* set WUF_CFG & WUF_MASK for IPv4 Multicast */
465362306a36Sopenharmony_ci		crc = lan78xx_wakeframe_crc16(ipv4_multicast, 3);
465462306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_CFG(mask_index),
465562306a36Sopenharmony_ci					WUF_CFGX_EN_ |
465662306a36Sopenharmony_ci					WUF_CFGX_TYPE_MCAST_ |
465762306a36Sopenharmony_ci					(0 << WUF_CFGX_OFFSET_SHIFT_) |
465862306a36Sopenharmony_ci					(crc & WUF_CFGX_CRC16_MASK_));
465962306a36Sopenharmony_ci		if (ret < 0)
466062306a36Sopenharmony_ci			return ret;
466162306a36Sopenharmony_ci
466262306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK0(mask_index), 7);
466362306a36Sopenharmony_ci		if (ret < 0)
466462306a36Sopenharmony_ci			return ret;
466562306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0);
466662306a36Sopenharmony_ci		if (ret < 0)
466762306a36Sopenharmony_ci			return ret;
466862306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0);
466962306a36Sopenharmony_ci		if (ret < 0)
467062306a36Sopenharmony_ci			return ret;
467162306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0);
467262306a36Sopenharmony_ci		if (ret < 0)
467362306a36Sopenharmony_ci			return ret;
467462306a36Sopenharmony_ci
467562306a36Sopenharmony_ci		mask_index++;
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci		/* for IPv6 Multicast */
467862306a36Sopenharmony_ci		crc = lan78xx_wakeframe_crc16(ipv6_multicast, 2);
467962306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_CFG(mask_index),
468062306a36Sopenharmony_ci					WUF_CFGX_EN_ |
468162306a36Sopenharmony_ci					WUF_CFGX_TYPE_MCAST_ |
468262306a36Sopenharmony_ci					(0 << WUF_CFGX_OFFSET_SHIFT_) |
468362306a36Sopenharmony_ci					(crc & WUF_CFGX_CRC16_MASK_));
468462306a36Sopenharmony_ci		if (ret < 0)
468562306a36Sopenharmony_ci			return ret;
468662306a36Sopenharmony_ci
468762306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK0(mask_index), 3);
468862306a36Sopenharmony_ci		if (ret < 0)
468962306a36Sopenharmony_ci			return ret;
469062306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0);
469162306a36Sopenharmony_ci		if (ret < 0)
469262306a36Sopenharmony_ci			return ret;
469362306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0);
469462306a36Sopenharmony_ci		if (ret < 0)
469562306a36Sopenharmony_ci			return ret;
469662306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0);
469762306a36Sopenharmony_ci		if (ret < 0)
469862306a36Sopenharmony_ci			return ret;
469962306a36Sopenharmony_ci
470062306a36Sopenharmony_ci		mask_index++;
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
470362306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
470462306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_;
470562306a36Sopenharmony_ci	}
470662306a36Sopenharmony_ci	if (wol & WAKE_UCAST) {
470762306a36Sopenharmony_ci		temp_wucsr |= WUCSR_PFDA_EN_;
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
471062306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
471162306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_;
471262306a36Sopenharmony_ci	}
471362306a36Sopenharmony_ci	if (wol & WAKE_ARP) {
471462306a36Sopenharmony_ci		temp_wucsr |= WUCSR_WAKE_EN_;
471562306a36Sopenharmony_ci
471662306a36Sopenharmony_ci		/* set WUF_CFG & WUF_MASK
471762306a36Sopenharmony_ci		 * for packettype (offset 12,13) = ARP (0x0806)
471862306a36Sopenharmony_ci		 */
471962306a36Sopenharmony_ci		crc = lan78xx_wakeframe_crc16(arp_type, 2);
472062306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_CFG(mask_index),
472162306a36Sopenharmony_ci					WUF_CFGX_EN_ |
472262306a36Sopenharmony_ci					WUF_CFGX_TYPE_ALL_ |
472362306a36Sopenharmony_ci					(0 << WUF_CFGX_OFFSET_SHIFT_) |
472462306a36Sopenharmony_ci					(crc & WUF_CFGX_CRC16_MASK_));
472562306a36Sopenharmony_ci		if (ret < 0)
472662306a36Sopenharmony_ci			return ret;
472762306a36Sopenharmony_ci
472862306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK0(mask_index), 0x3000);
472962306a36Sopenharmony_ci		if (ret < 0)
473062306a36Sopenharmony_ci			return ret;
473162306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK1(mask_index), 0);
473262306a36Sopenharmony_ci		if (ret < 0)
473362306a36Sopenharmony_ci			return ret;
473462306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK2(mask_index), 0);
473562306a36Sopenharmony_ci		if (ret < 0)
473662306a36Sopenharmony_ci			return ret;
473762306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUF_MASK3(mask_index), 0);
473862306a36Sopenharmony_ci		if (ret < 0)
473962306a36Sopenharmony_ci			return ret;
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci		mask_index++;
474262306a36Sopenharmony_ci
474362306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
474462306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
474562306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_;
474662306a36Sopenharmony_ci	}
474762306a36Sopenharmony_ci
474862306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR, temp_wucsr);
474962306a36Sopenharmony_ci	if (ret < 0)
475062306a36Sopenharmony_ci		return ret;
475162306a36Sopenharmony_ci
475262306a36Sopenharmony_ci	/* when multiple WOL bits are set */
475362306a36Sopenharmony_ci	if (hweight_long((unsigned long)wol) > 1) {
475462306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_WOL_EN_;
475562306a36Sopenharmony_ci		temp_pmt_ctl &= ~PMT_CTL_SUS_MODE_MASK_;
475662306a36Sopenharmony_ci		temp_pmt_ctl |= PMT_CTL_SUS_MODE_0_;
475762306a36Sopenharmony_ci	}
475862306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, PMT_CTL, temp_pmt_ctl);
475962306a36Sopenharmony_ci	if (ret < 0)
476062306a36Sopenharmony_ci		return ret;
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	/* clear WUPS */
476362306a36Sopenharmony_ci	ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
476462306a36Sopenharmony_ci	if (ret < 0)
476562306a36Sopenharmony_ci		return ret;
476662306a36Sopenharmony_ci
476762306a36Sopenharmony_ci	buf |= PMT_CTL_WUPS_MASK_;
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, PMT_CTL, buf);
477062306a36Sopenharmony_ci	if (ret < 0)
477162306a36Sopenharmony_ci		return ret;
477262306a36Sopenharmony_ci
477362306a36Sopenharmony_ci	ret = lan78xx_start_rx_path(dev);
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	return ret;
477662306a36Sopenharmony_ci}
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_cistatic int lan78xx_suspend(struct usb_interface *intf, pm_message_t message)
477962306a36Sopenharmony_ci{
478062306a36Sopenharmony_ci	struct lan78xx_net *dev = usb_get_intfdata(intf);
478162306a36Sopenharmony_ci	bool dev_open;
478262306a36Sopenharmony_ci	int ret;
478362306a36Sopenharmony_ci
478462306a36Sopenharmony_ci	mutex_lock(&dev->dev_mutex);
478562306a36Sopenharmony_ci
478662306a36Sopenharmony_ci	netif_dbg(dev, ifdown, dev->net,
478762306a36Sopenharmony_ci		  "suspending: pm event %#x", message.event);
478862306a36Sopenharmony_ci
478962306a36Sopenharmony_ci	dev_open = test_bit(EVENT_DEV_OPEN, &dev->flags);
479062306a36Sopenharmony_ci
479162306a36Sopenharmony_ci	if (dev_open) {
479262306a36Sopenharmony_ci		spin_lock_irq(&dev->txq.lock);
479362306a36Sopenharmony_ci		/* don't autosuspend while transmitting */
479462306a36Sopenharmony_ci		if ((skb_queue_len(&dev->txq) ||
479562306a36Sopenharmony_ci		     skb_queue_len(&dev->txq_pend)) &&
479662306a36Sopenharmony_ci		    PMSG_IS_AUTO(message)) {
479762306a36Sopenharmony_ci			spin_unlock_irq(&dev->txq.lock);
479862306a36Sopenharmony_ci			ret = -EBUSY;
479962306a36Sopenharmony_ci			goto out;
480062306a36Sopenharmony_ci		} else {
480162306a36Sopenharmony_ci			set_bit(EVENT_DEV_ASLEEP, &dev->flags);
480262306a36Sopenharmony_ci			spin_unlock_irq(&dev->txq.lock);
480362306a36Sopenharmony_ci		}
480462306a36Sopenharmony_ci
480562306a36Sopenharmony_ci		/* stop RX */
480662306a36Sopenharmony_ci		ret = lan78xx_stop_rx_path(dev);
480762306a36Sopenharmony_ci		if (ret < 0)
480862306a36Sopenharmony_ci			goto out;
480962306a36Sopenharmony_ci
481062306a36Sopenharmony_ci		ret = lan78xx_flush_rx_fifo(dev);
481162306a36Sopenharmony_ci		if (ret < 0)
481262306a36Sopenharmony_ci			goto out;
481362306a36Sopenharmony_ci
481462306a36Sopenharmony_ci		/* stop Tx */
481562306a36Sopenharmony_ci		ret = lan78xx_stop_tx_path(dev);
481662306a36Sopenharmony_ci		if (ret < 0)
481762306a36Sopenharmony_ci			goto out;
481862306a36Sopenharmony_ci
481962306a36Sopenharmony_ci		/* empty out the Rx and Tx queues */
482062306a36Sopenharmony_ci		netif_device_detach(dev->net);
482162306a36Sopenharmony_ci		lan78xx_terminate_urbs(dev);
482262306a36Sopenharmony_ci		usb_kill_urb(dev->urb_intr);
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_ci		/* reattach */
482562306a36Sopenharmony_ci		netif_device_attach(dev->net);
482662306a36Sopenharmony_ci
482762306a36Sopenharmony_ci		del_timer(&dev->stat_monitor);
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci		if (PMSG_IS_AUTO(message)) {
483062306a36Sopenharmony_ci			ret = lan78xx_set_auto_suspend(dev);
483162306a36Sopenharmony_ci			if (ret < 0)
483262306a36Sopenharmony_ci				goto out;
483362306a36Sopenharmony_ci		} else {
483462306a36Sopenharmony_ci			struct lan78xx_priv *pdata;
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci			pdata = (struct lan78xx_priv *)(dev->data[0]);
483762306a36Sopenharmony_ci			netif_carrier_off(dev->net);
483862306a36Sopenharmony_ci			ret = lan78xx_set_suspend(dev, pdata->wol);
483962306a36Sopenharmony_ci			if (ret < 0)
484062306a36Sopenharmony_ci				goto out;
484162306a36Sopenharmony_ci		}
484262306a36Sopenharmony_ci	} else {
484362306a36Sopenharmony_ci		/* Interface is down; don't allow WOL and PHY
484462306a36Sopenharmony_ci		 * events to wake up the host
484562306a36Sopenharmony_ci		 */
484662306a36Sopenharmony_ci		u32 buf;
484762306a36Sopenharmony_ci
484862306a36Sopenharmony_ci		set_bit(EVENT_DEV_ASLEEP, &dev->flags);
484962306a36Sopenharmony_ci
485062306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUCSR, 0);
485162306a36Sopenharmony_ci		if (ret < 0)
485262306a36Sopenharmony_ci			goto out;
485362306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, WUCSR2, 0);
485462306a36Sopenharmony_ci		if (ret < 0)
485562306a36Sopenharmony_ci			goto out;
485662306a36Sopenharmony_ci
485762306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
485862306a36Sopenharmony_ci		if (ret < 0)
485962306a36Sopenharmony_ci			goto out;
486062306a36Sopenharmony_ci
486162306a36Sopenharmony_ci		buf &= ~PMT_CTL_RES_CLR_WKP_EN_;
486262306a36Sopenharmony_ci		buf |= PMT_CTL_RES_CLR_WKP_STS_;
486362306a36Sopenharmony_ci		buf &= ~PMT_CTL_SUS_MODE_MASK_;
486462306a36Sopenharmony_ci		buf |= PMT_CTL_SUS_MODE_3_;
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, PMT_CTL, buf);
486762306a36Sopenharmony_ci		if (ret < 0)
486862306a36Sopenharmony_ci			goto out;
486962306a36Sopenharmony_ci
487062306a36Sopenharmony_ci		ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
487162306a36Sopenharmony_ci		if (ret < 0)
487262306a36Sopenharmony_ci			goto out;
487362306a36Sopenharmony_ci
487462306a36Sopenharmony_ci		buf |= PMT_CTL_WUPS_MASK_;
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci		ret = lan78xx_write_reg(dev, PMT_CTL, buf);
487762306a36Sopenharmony_ci		if (ret < 0)
487862306a36Sopenharmony_ci			goto out;
487962306a36Sopenharmony_ci	}
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_ci	ret = 0;
488262306a36Sopenharmony_ciout:
488362306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	return ret;
488662306a36Sopenharmony_ci}
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_cistatic bool lan78xx_submit_deferred_urbs(struct lan78xx_net *dev)
488962306a36Sopenharmony_ci{
489062306a36Sopenharmony_ci	bool pipe_halted = false;
489162306a36Sopenharmony_ci	struct urb *urb;
489262306a36Sopenharmony_ci
489362306a36Sopenharmony_ci	while ((urb = usb_get_from_anchor(&dev->deferred))) {
489462306a36Sopenharmony_ci		struct sk_buff *skb = urb->context;
489562306a36Sopenharmony_ci		int ret;
489662306a36Sopenharmony_ci
489762306a36Sopenharmony_ci		if (!netif_device_present(dev->net) ||
489862306a36Sopenharmony_ci		    !netif_carrier_ok(dev->net) ||
489962306a36Sopenharmony_ci		    pipe_halted) {
490062306a36Sopenharmony_ci			lan78xx_release_tx_buf(dev, skb);
490162306a36Sopenharmony_ci			continue;
490262306a36Sopenharmony_ci		}
490362306a36Sopenharmony_ci
490462306a36Sopenharmony_ci		ret = usb_submit_urb(urb, GFP_ATOMIC);
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_ci		if (ret == 0) {
490762306a36Sopenharmony_ci			netif_trans_update(dev->net);
490862306a36Sopenharmony_ci			lan78xx_queue_skb(&dev->txq, skb, tx_start);
490962306a36Sopenharmony_ci		} else {
491062306a36Sopenharmony_ci			if (ret == -EPIPE) {
491162306a36Sopenharmony_ci				netif_stop_queue(dev->net);
491262306a36Sopenharmony_ci				pipe_halted = true;
491362306a36Sopenharmony_ci			} else if (ret == -ENODEV) {
491462306a36Sopenharmony_ci				netif_device_detach(dev->net);
491562306a36Sopenharmony_ci			}
491662306a36Sopenharmony_ci
491762306a36Sopenharmony_ci			lan78xx_release_tx_buf(dev, skb);
491862306a36Sopenharmony_ci		}
491962306a36Sopenharmony_ci	}
492062306a36Sopenharmony_ci
492162306a36Sopenharmony_ci	return pipe_halted;
492262306a36Sopenharmony_ci}
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_cistatic int lan78xx_resume(struct usb_interface *intf)
492562306a36Sopenharmony_ci{
492662306a36Sopenharmony_ci	struct lan78xx_net *dev = usb_get_intfdata(intf);
492762306a36Sopenharmony_ci	bool dev_open;
492862306a36Sopenharmony_ci	int ret;
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci	mutex_lock(&dev->dev_mutex);
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	netif_dbg(dev, ifup, dev->net, "resuming device");
493362306a36Sopenharmony_ci
493462306a36Sopenharmony_ci	dev_open = test_bit(EVENT_DEV_OPEN, &dev->flags);
493562306a36Sopenharmony_ci
493662306a36Sopenharmony_ci	if (dev_open) {
493762306a36Sopenharmony_ci		bool pipe_halted = false;
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci		ret = lan78xx_flush_tx_fifo(dev);
494062306a36Sopenharmony_ci		if (ret < 0)
494162306a36Sopenharmony_ci			goto out;
494262306a36Sopenharmony_ci
494362306a36Sopenharmony_ci		if (dev->urb_intr) {
494462306a36Sopenharmony_ci			int ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL);
494562306a36Sopenharmony_ci
494662306a36Sopenharmony_ci			if (ret < 0) {
494762306a36Sopenharmony_ci				if (ret == -ENODEV)
494862306a36Sopenharmony_ci					netif_device_detach(dev->net);
494962306a36Sopenharmony_ci				netdev_warn(dev->net, "Failed to submit intr URB");
495062306a36Sopenharmony_ci			}
495162306a36Sopenharmony_ci		}
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci		spin_lock_irq(&dev->txq.lock);
495462306a36Sopenharmony_ci
495562306a36Sopenharmony_ci		if (netif_device_present(dev->net)) {
495662306a36Sopenharmony_ci			pipe_halted = lan78xx_submit_deferred_urbs(dev);
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci			if (pipe_halted)
495962306a36Sopenharmony_ci				lan78xx_defer_kevent(dev, EVENT_TX_HALT);
496062306a36Sopenharmony_ci		}
496162306a36Sopenharmony_ci
496262306a36Sopenharmony_ci		clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
496362306a36Sopenharmony_ci
496462306a36Sopenharmony_ci		spin_unlock_irq(&dev->txq.lock);
496562306a36Sopenharmony_ci
496662306a36Sopenharmony_ci		if (!pipe_halted &&
496762306a36Sopenharmony_ci		    netif_device_present(dev->net) &&
496862306a36Sopenharmony_ci		    (lan78xx_tx_pend_data_len(dev) < lan78xx_tx_urb_space(dev)))
496962306a36Sopenharmony_ci			netif_start_queue(dev->net);
497062306a36Sopenharmony_ci
497162306a36Sopenharmony_ci		ret = lan78xx_start_tx_path(dev);
497262306a36Sopenharmony_ci		if (ret < 0)
497362306a36Sopenharmony_ci			goto out;
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci		napi_schedule(&dev->napi);
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci		if (!timer_pending(&dev->stat_monitor)) {
497862306a36Sopenharmony_ci			dev->delta = 1;
497962306a36Sopenharmony_ci			mod_timer(&dev->stat_monitor,
498062306a36Sopenharmony_ci				  jiffies + STAT_UPDATE_TIMER);
498162306a36Sopenharmony_ci		}
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci	} else {
498462306a36Sopenharmony_ci		clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
498562306a36Sopenharmony_ci	}
498662306a36Sopenharmony_ci
498762306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR2, 0);
498862306a36Sopenharmony_ci	if (ret < 0)
498962306a36Sopenharmony_ci		goto out;
499062306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR, 0);
499162306a36Sopenharmony_ci	if (ret < 0)
499262306a36Sopenharmony_ci		goto out;
499362306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WK_SRC, 0xFFF1FF1FUL);
499462306a36Sopenharmony_ci	if (ret < 0)
499562306a36Sopenharmony_ci		goto out;
499662306a36Sopenharmony_ci
499762306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR2, WUCSR2_NS_RCD_ |
499862306a36Sopenharmony_ci					     WUCSR2_ARP_RCD_ |
499962306a36Sopenharmony_ci					     WUCSR2_IPV6_TCPSYN_RCD_ |
500062306a36Sopenharmony_ci					     WUCSR2_IPV4_TCPSYN_RCD_);
500162306a36Sopenharmony_ci	if (ret < 0)
500262306a36Sopenharmony_ci		goto out;
500362306a36Sopenharmony_ci
500462306a36Sopenharmony_ci	ret = lan78xx_write_reg(dev, WUCSR, WUCSR_EEE_TX_WAKE_ |
500562306a36Sopenharmony_ci					    WUCSR_EEE_RX_WAKE_ |
500662306a36Sopenharmony_ci					    WUCSR_PFDA_FR_ |
500762306a36Sopenharmony_ci					    WUCSR_RFE_WAKE_FR_ |
500862306a36Sopenharmony_ci					    WUCSR_WUFR_ |
500962306a36Sopenharmony_ci					    WUCSR_MPR_ |
501062306a36Sopenharmony_ci					    WUCSR_BCST_FR_);
501162306a36Sopenharmony_ci	if (ret < 0)
501262306a36Sopenharmony_ci		goto out;
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci	ret = 0;
501562306a36Sopenharmony_ciout:
501662306a36Sopenharmony_ci	mutex_unlock(&dev->dev_mutex);
501762306a36Sopenharmony_ci
501862306a36Sopenharmony_ci	return ret;
501962306a36Sopenharmony_ci}
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_cistatic int lan78xx_reset_resume(struct usb_interface *intf)
502262306a36Sopenharmony_ci{
502362306a36Sopenharmony_ci	struct lan78xx_net *dev = usb_get_intfdata(intf);
502462306a36Sopenharmony_ci	int ret;
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_ci	netif_dbg(dev, ifup, dev->net, "(reset) resuming device");
502762306a36Sopenharmony_ci
502862306a36Sopenharmony_ci	ret = lan78xx_reset(dev);
502962306a36Sopenharmony_ci	if (ret < 0)
503062306a36Sopenharmony_ci		return ret;
503162306a36Sopenharmony_ci
503262306a36Sopenharmony_ci	phy_start(dev->net->phydev);
503362306a36Sopenharmony_ci
503462306a36Sopenharmony_ci	ret = lan78xx_resume(intf);
503562306a36Sopenharmony_ci
503662306a36Sopenharmony_ci	return ret;
503762306a36Sopenharmony_ci}
503862306a36Sopenharmony_ci
503962306a36Sopenharmony_cistatic const struct usb_device_id products[] = {
504062306a36Sopenharmony_ci	{
504162306a36Sopenharmony_ci	/* LAN7800 USB Gigabit Ethernet Device */
504262306a36Sopenharmony_ci	USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7800_USB_PRODUCT_ID),
504362306a36Sopenharmony_ci	},
504462306a36Sopenharmony_ci	{
504562306a36Sopenharmony_ci	/* LAN7850 USB Gigabit Ethernet Device */
504662306a36Sopenharmony_ci	USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7850_USB_PRODUCT_ID),
504762306a36Sopenharmony_ci	},
504862306a36Sopenharmony_ci	{
504962306a36Sopenharmony_ci	/* LAN7801 USB Gigabit Ethernet Device */
505062306a36Sopenharmony_ci	USB_DEVICE(LAN78XX_USB_VENDOR_ID, LAN7801_USB_PRODUCT_ID),
505162306a36Sopenharmony_ci	},
505262306a36Sopenharmony_ci	{
505362306a36Sopenharmony_ci	/* ATM2-AF USB Gigabit Ethernet Device */
505462306a36Sopenharmony_ci	USB_DEVICE(AT29M2AF_USB_VENDOR_ID, AT29M2AF_USB_PRODUCT_ID),
505562306a36Sopenharmony_ci	},
505662306a36Sopenharmony_ci	{},
505762306a36Sopenharmony_ci};
505862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products);
505962306a36Sopenharmony_ci
506062306a36Sopenharmony_cistatic struct usb_driver lan78xx_driver = {
506162306a36Sopenharmony_ci	.name			= DRIVER_NAME,
506262306a36Sopenharmony_ci	.id_table		= products,
506362306a36Sopenharmony_ci	.probe			= lan78xx_probe,
506462306a36Sopenharmony_ci	.disconnect		= lan78xx_disconnect,
506562306a36Sopenharmony_ci	.suspend		= lan78xx_suspend,
506662306a36Sopenharmony_ci	.resume			= lan78xx_resume,
506762306a36Sopenharmony_ci	.reset_resume		= lan78xx_reset_resume,
506862306a36Sopenharmony_ci	.supports_autosuspend	= 1,
506962306a36Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
507062306a36Sopenharmony_ci};
507162306a36Sopenharmony_ci
507262306a36Sopenharmony_cimodule_usb_driver(lan78xx_driver);
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
507562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
507662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5077