11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2d0cad871SSteve Glendinning /***************************************************************************
3d0cad871SSteve Glendinning *
4d0cad871SSteve Glendinning * Copyright (C) 2007-2010 SMSC
5d0cad871SSteve Glendinning *
6d0cad871SSteve Glendinning *****************************************************************************/
7d0cad871SSteve Glendinning
8d0cad871SSteve Glendinning #include <linux/module.h>
9d0cad871SSteve Glendinning #include <linux/kmod.h>
10d0cad871SSteve Glendinning #include <linux/netdevice.h>
11d0cad871SSteve Glendinning #include <linux/etherdevice.h>
12d0cad871SSteve Glendinning #include <linux/ethtool.h>
13d0cad871SSteve Glendinning #include <linux/mii.h>
14d0cad871SSteve Glendinning #include <linux/usb.h>
15899a391bSSteve Glendinning #include <linux/bitrev.h>
16899a391bSSteve Glendinning #include <linux/crc16.h>
17d0cad871SSteve Glendinning #include <linux/crc32.h>
18d0cad871SSteve Glendinning #include <linux/usb/usbnet.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
20c489565bSArnd Bergmann #include <linux/of_net.h>
21d0cad871SSteve Glendinning #include "smsc75xx.h"
22d0cad871SSteve Glendinning
23d0cad871SSteve Glendinning #define SMSC_CHIPNAME "smsc75xx"
24d0cad871SSteve Glendinning #define SMSC_DRIVER_VERSION "1.0.0"
25d0cad871SSteve Glendinning #define HS_USB_PKT_SIZE (512)
26d0cad871SSteve Glendinning #define FS_USB_PKT_SIZE (64)
27d0cad871SSteve Glendinning #define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
28d0cad871SSteve Glendinning #define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE)
29d0cad871SSteve Glendinning #define DEFAULT_BULK_IN_DELAY (0x00002000)
30d0cad871SSteve Glendinning #define MAX_SINGLE_PACKET_SIZE (9000)
31d0cad871SSteve Glendinning #define LAN75XX_EEPROM_MAGIC (0x7500)
32d0cad871SSteve Glendinning #define EEPROM_MAC_OFFSET (0x01)
33d0cad871SSteve Glendinning #define DEFAULT_TX_CSUM_ENABLE (true)
34d0cad871SSteve Glendinning #define DEFAULT_RX_CSUM_ENABLE (true)
35d0cad871SSteve Glendinning #define SMSC75XX_INTERNAL_PHY_ID (1)
36d0cad871SSteve Glendinning #define SMSC75XX_TX_OVERHEAD (8)
37d0cad871SSteve Glendinning #define MAX_RX_FIFO_SIZE (20 * 1024)
38d0cad871SSteve Glendinning #define MAX_TX_FIFO_SIZE (12 * 1024)
39d0cad871SSteve Glendinning #define USB_VENDOR_ID_SMSC (0x0424)
40d0cad871SSteve Glendinning #define USB_PRODUCT_ID_LAN7500 (0x7500)
41d0cad871SSteve Glendinning #define USB_PRODUCT_ID_LAN7505 (0x7505)
42ea1649deSNico Erfurth #define RXW_PADDING 2
43f329ccdcSSteve Glendinning #define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \
44899a391bSSteve Glendinning WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)
45d0cad871SSteve Glendinning
46b4cdea9cSSteve Glendinning #define SUSPEND_SUSPEND0 (0x01)
47b4cdea9cSSteve Glendinning #define SUSPEND_SUSPEND1 (0x02)
48b4cdea9cSSteve Glendinning #define SUSPEND_SUSPEND2 (0x04)
49b4cdea9cSSteve Glendinning #define SUSPEND_SUSPEND3 (0x08)
50b4cdea9cSSteve Glendinning #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
51b4cdea9cSSteve Glendinning SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
52b4cdea9cSSteve Glendinning
53d0cad871SSteve Glendinning struct smsc75xx_priv {
54d0cad871SSteve Glendinning struct usbnet *dev;
55d0cad871SSteve Glendinning u32 rfe_ctl;
566c636503SSteve Glendinning u32 wolopts;
57d0cad871SSteve Glendinning u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN];
58d0cad871SSteve Glendinning struct mutex dataport_mutex;
59d0cad871SSteve Glendinning spinlock_t rfe_ctl_lock;
60d0cad871SSteve Glendinning struct work_struct set_multicast;
61b4cdea9cSSteve Glendinning u8 suspend_flags;
62d0cad871SSteve Glendinning };
63d0cad871SSteve Glendinning
64eb939922SRusty Russell static bool turbo_mode = true;
65d0cad871SSteve Glendinning module_param(turbo_mode, bool, 0644);
66d0cad871SSteve Glendinning MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
67d0cad871SSteve Glendinning
68d461e3daSYuiko Oshino static int smsc75xx_link_ok_nopm(struct usbnet *dev);
69d461e3daSYuiko Oshino static int smsc75xx_phy_gig_workaround(struct usbnet *dev);
70d461e3daSYuiko Oshino
__smsc75xx_read_reg(struct usbnet * dev,u32 index,u32 * data,int in_pm)7147bbea41SMing Lei static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index,
7247bbea41SMing Lei u32 *data, int in_pm)
73d0cad871SSteve Glendinning {
742b2e41e3SMing Lei u32 buf;
75d0cad871SSteve Glendinning int ret;
7647bbea41SMing Lei int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
77d0cad871SSteve Glendinning
78d0cad871SSteve Glendinning BUG_ON(!dev);
79d0cad871SSteve Glendinning
8047bbea41SMing Lei if (!in_pm)
8147bbea41SMing Lei fn = usbnet_read_cmd;
8247bbea41SMing Lei else
8347bbea41SMing Lei fn = usbnet_read_cmd_nopm;
8447bbea41SMing Lei
8547bbea41SMing Lei ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN
8647bbea41SMing Lei | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
872b2e41e3SMing Lei 0, index, &buf, 4);
88e9c65989SShigeru Yoshida if (unlikely(ret < 4)) {
89e9c65989SShigeru Yoshida ret = ret < 0 ? ret : -ENODATA;
90e9c65989SShigeru Yoshida
911e1d7412SJoe Perches netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n",
921e1d7412SJoe Perches index, ret);
9358ef6a3fSDan Carpenter return ret;
9458ef6a3fSDan Carpenter }
95d0cad871SSteve Glendinning
962b2e41e3SMing Lei le32_to_cpus(&buf);
972b2e41e3SMing Lei *data = buf;
98d0cad871SSteve Glendinning
99d0cad871SSteve Glendinning return ret;
100d0cad871SSteve Glendinning }
101d0cad871SSteve Glendinning
__smsc75xx_write_reg(struct usbnet * dev,u32 index,u32 data,int in_pm)10247bbea41SMing Lei static int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index,
10347bbea41SMing Lei u32 data, int in_pm)
104d0cad871SSteve Glendinning {
1052b2e41e3SMing Lei u32 buf;
106d0cad871SSteve Glendinning int ret;
10747bbea41SMing Lei int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
108d0cad871SSteve Glendinning
109d0cad871SSteve Glendinning BUG_ON(!dev);
110d0cad871SSteve Glendinning
11147bbea41SMing Lei if (!in_pm)
11247bbea41SMing Lei fn = usbnet_write_cmd;
11347bbea41SMing Lei else
11447bbea41SMing Lei fn = usbnet_write_cmd_nopm;
11547bbea41SMing Lei
1162b2e41e3SMing Lei buf = data;
1172b2e41e3SMing Lei cpu_to_le32s(&buf);
118d0cad871SSteve Glendinning
11947bbea41SMing Lei ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT
12047bbea41SMing Lei | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1212b2e41e3SMing Lei 0, index, &buf, 4);
122d0cad871SSteve Glendinning if (unlikely(ret < 0))
1231e1d7412SJoe Perches netdev_warn(dev->net, "Failed to write reg index 0x%08x: %d\n",
1241e1d7412SJoe Perches index, ret);
125d0cad871SSteve Glendinning
126d0cad871SSteve Glendinning return ret;
127d0cad871SSteve Glendinning }
128d0cad871SSteve Glendinning
smsc75xx_read_reg_nopm(struct usbnet * dev,u32 index,u32 * data)12947bbea41SMing Lei static int __must_check smsc75xx_read_reg_nopm(struct usbnet *dev, u32 index,
13047bbea41SMing Lei u32 *data)
13147bbea41SMing Lei {
13247bbea41SMing Lei return __smsc75xx_read_reg(dev, index, data, 1);
13347bbea41SMing Lei }
13447bbea41SMing Lei
smsc75xx_write_reg_nopm(struct usbnet * dev,u32 index,u32 data)13547bbea41SMing Lei static int __must_check smsc75xx_write_reg_nopm(struct usbnet *dev, u32 index,
13647bbea41SMing Lei u32 data)
13747bbea41SMing Lei {
13847bbea41SMing Lei return __smsc75xx_write_reg(dev, index, data, 1);
13947bbea41SMing Lei }
14047bbea41SMing Lei
smsc75xx_read_reg(struct usbnet * dev,u32 index,u32 * data)14147bbea41SMing Lei static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index,
14247bbea41SMing Lei u32 *data)
14347bbea41SMing Lei {
14447bbea41SMing Lei return __smsc75xx_read_reg(dev, index, data, 0);
14547bbea41SMing Lei }
14647bbea41SMing Lei
smsc75xx_write_reg(struct usbnet * dev,u32 index,u32 data)14747bbea41SMing Lei static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index,
14847bbea41SMing Lei u32 data)
14947bbea41SMing Lei {
15047bbea41SMing Lei return __smsc75xx_write_reg(dev, index, data, 0);
15147bbea41SMing Lei }
15247bbea41SMing Lei
153d0cad871SSteve Glendinning /* Loop until the read is completed with timeout
154d0cad871SSteve Glendinning * called with phy_mutex held */
__smsc75xx_phy_wait_not_busy(struct usbnet * dev,int in_pm)155f329ccdcSSteve Glendinning static __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev,
156f329ccdcSSteve Glendinning int in_pm)
157d0cad871SSteve Glendinning {
158d0cad871SSteve Glendinning unsigned long start_time = jiffies;
159d0cad871SSteve Glendinning u32 val;
160d0cad871SSteve Glendinning int ret;
161d0cad871SSteve Glendinning
162d0cad871SSteve Glendinning do {
163f329ccdcSSteve Glendinning ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm);
164e3c678e6SSteve Glendinning if (ret < 0) {
165e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading MII_ACCESS\n");
166e3c678e6SSteve Glendinning return ret;
167e3c678e6SSteve Glendinning }
168d0cad871SSteve Glendinning
169d0cad871SSteve Glendinning if (!(val & MII_ACCESS_BUSY))
170d0cad871SSteve Glendinning return 0;
171d0cad871SSteve Glendinning } while (!time_after(jiffies, start_time + HZ));
172d0cad871SSteve Glendinning
173d0cad871SSteve Glendinning return -EIO;
174d0cad871SSteve Glendinning }
175d0cad871SSteve Glendinning
__smsc75xx_mdio_read(struct net_device * netdev,int phy_id,int idx,int in_pm)176f329ccdcSSteve Glendinning static int __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx,
177f329ccdcSSteve Glendinning int in_pm)
178d0cad871SSteve Glendinning {
179d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
180d0cad871SSteve Glendinning u32 val, addr;
181d0cad871SSteve Glendinning int ret;
182d0cad871SSteve Glendinning
183d0cad871SSteve Glendinning mutex_lock(&dev->phy_mutex);
184d0cad871SSteve Glendinning
185d0cad871SSteve Glendinning /* confirm MII not busy */
186f329ccdcSSteve Glendinning ret = __smsc75xx_phy_wait_not_busy(dev, in_pm);
187e3c678e6SSteve Glendinning if (ret < 0) {
188e3c678e6SSteve Glendinning netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_read\n");
189e3c678e6SSteve Glendinning goto done;
190e3c678e6SSteve Glendinning }
191d0cad871SSteve Glendinning
192d0cad871SSteve Glendinning /* set the address, index & direction (read from PHY) */
193d0cad871SSteve Glendinning phy_id &= dev->mii.phy_id_mask;
194d0cad871SSteve Glendinning idx &= dev->mii.reg_num_mask;
195d0cad871SSteve Glendinning addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR)
196d0cad871SSteve Glendinning | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR)
197cb8722d3SSteve Glendinning | MII_ACCESS_READ | MII_ACCESS_BUSY;
198f329ccdcSSteve Glendinning ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm);
199e3c678e6SSteve Glendinning if (ret < 0) {
200e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing MII_ACCESS\n");
201e3c678e6SSteve Glendinning goto done;
202e3c678e6SSteve Glendinning }
203d0cad871SSteve Glendinning
204f329ccdcSSteve Glendinning ret = __smsc75xx_phy_wait_not_busy(dev, in_pm);
205e3c678e6SSteve Glendinning if (ret < 0) {
206e3c678e6SSteve Glendinning netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx);
207e3c678e6SSteve Glendinning goto done;
208e3c678e6SSteve Glendinning }
209d0cad871SSteve Glendinning
210f329ccdcSSteve Glendinning ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm);
211e3c678e6SSteve Glendinning if (ret < 0) {
212e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading MII_DATA\n");
213e3c678e6SSteve Glendinning goto done;
214e3c678e6SSteve Glendinning }
215d0cad871SSteve Glendinning
216d0cad871SSteve Glendinning ret = (u16)(val & 0xFFFF);
217d0cad871SSteve Glendinning
218d0cad871SSteve Glendinning done:
219d0cad871SSteve Glendinning mutex_unlock(&dev->phy_mutex);
220d0cad871SSteve Glendinning return ret;
221d0cad871SSteve Glendinning }
222d0cad871SSteve Glendinning
__smsc75xx_mdio_write(struct net_device * netdev,int phy_id,int idx,int regval,int in_pm)223f329ccdcSSteve Glendinning static void __smsc75xx_mdio_write(struct net_device *netdev, int phy_id,
224f329ccdcSSteve Glendinning int idx, int regval, int in_pm)
225d0cad871SSteve Glendinning {
226d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
227d0cad871SSteve Glendinning u32 val, addr;
228d0cad871SSteve Glendinning int ret;
229d0cad871SSteve Glendinning
230d0cad871SSteve Glendinning mutex_lock(&dev->phy_mutex);
231d0cad871SSteve Glendinning
232d0cad871SSteve Glendinning /* confirm MII not busy */
233f329ccdcSSteve Glendinning ret = __smsc75xx_phy_wait_not_busy(dev, in_pm);
234e3c678e6SSteve Glendinning if (ret < 0) {
235e3c678e6SSteve Glendinning netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_write\n");
236e3c678e6SSteve Glendinning goto done;
237e3c678e6SSteve Glendinning }
238d0cad871SSteve Glendinning
239d0cad871SSteve Glendinning val = regval;
240f329ccdcSSteve Glendinning ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm);
241e3c678e6SSteve Glendinning if (ret < 0) {
242e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing MII_DATA\n");
243e3c678e6SSteve Glendinning goto done;
244e3c678e6SSteve Glendinning }
245d0cad871SSteve Glendinning
246d0cad871SSteve Glendinning /* set the address, index & direction (write to PHY) */
247d0cad871SSteve Glendinning phy_id &= dev->mii.phy_id_mask;
248d0cad871SSteve Glendinning idx &= dev->mii.reg_num_mask;
249d0cad871SSteve Glendinning addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR)
250d0cad871SSteve Glendinning | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR)
251cb8722d3SSteve Glendinning | MII_ACCESS_WRITE | MII_ACCESS_BUSY;
252f329ccdcSSteve Glendinning ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm);
253e3c678e6SSteve Glendinning if (ret < 0) {
254e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing MII_ACCESS\n");
255e3c678e6SSteve Glendinning goto done;
256e3c678e6SSteve Glendinning }
257d0cad871SSteve Glendinning
258f329ccdcSSteve Glendinning ret = __smsc75xx_phy_wait_not_busy(dev, in_pm);
259e3c678e6SSteve Glendinning if (ret < 0) {
260e3c678e6SSteve Glendinning netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx);
261e3c678e6SSteve Glendinning goto done;
262e3c678e6SSteve Glendinning }
263d0cad871SSteve Glendinning
264d0cad871SSteve Glendinning done:
265d0cad871SSteve Glendinning mutex_unlock(&dev->phy_mutex);
266d0cad871SSteve Glendinning }
267d0cad871SSteve Glendinning
smsc75xx_mdio_read_nopm(struct net_device * netdev,int phy_id,int idx)268f329ccdcSSteve Glendinning static int smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id,
269f329ccdcSSteve Glendinning int idx)
270f329ccdcSSteve Glendinning {
271f329ccdcSSteve Glendinning return __smsc75xx_mdio_read(netdev, phy_id, idx, 1);
272f329ccdcSSteve Glendinning }
273f329ccdcSSteve Glendinning
smsc75xx_mdio_write_nopm(struct net_device * netdev,int phy_id,int idx,int regval)274f329ccdcSSteve Glendinning static void smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id,
275f329ccdcSSteve Glendinning int idx, int regval)
276f329ccdcSSteve Glendinning {
277f329ccdcSSteve Glendinning __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1);
278f329ccdcSSteve Glendinning }
279f329ccdcSSteve Glendinning
smsc75xx_mdio_read(struct net_device * netdev,int phy_id,int idx)280f329ccdcSSteve Glendinning static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx)
281f329ccdcSSteve Glendinning {
282f329ccdcSSteve Glendinning return __smsc75xx_mdio_read(netdev, phy_id, idx, 0);
283f329ccdcSSteve Glendinning }
284f329ccdcSSteve Glendinning
smsc75xx_mdio_write(struct net_device * netdev,int phy_id,int idx,int regval)285f329ccdcSSteve Glendinning static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx,
286f329ccdcSSteve Glendinning int regval)
287f329ccdcSSteve Glendinning {
288f329ccdcSSteve Glendinning __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0);
289f329ccdcSSteve Glendinning }
290f329ccdcSSteve Glendinning
smsc75xx_wait_eeprom(struct usbnet * dev)291d0cad871SSteve Glendinning static int smsc75xx_wait_eeprom(struct usbnet *dev)
292d0cad871SSteve Glendinning {
293d0cad871SSteve Glendinning unsigned long start_time = jiffies;
294d0cad871SSteve Glendinning u32 val;
295d0cad871SSteve Glendinning int ret;
296d0cad871SSteve Glendinning
297d0cad871SSteve Glendinning do {
298d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, E2P_CMD, &val);
299e3c678e6SSteve Glendinning if (ret < 0) {
300e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading E2P_CMD\n");
301e3c678e6SSteve Glendinning return ret;
302e3c678e6SSteve Glendinning }
303d0cad871SSteve Glendinning
304d0cad871SSteve Glendinning if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT))
305d0cad871SSteve Glendinning break;
306d0cad871SSteve Glendinning udelay(40);
307d0cad871SSteve Glendinning } while (!time_after(jiffies, start_time + HZ));
308d0cad871SSteve Glendinning
309d0cad871SSteve Glendinning if (val & (E2P_CMD_TIMEOUT | E2P_CMD_BUSY)) {
3101e1d7412SJoe Perches netdev_warn(dev->net, "EEPROM read operation timeout\n");
311d0cad871SSteve Glendinning return -EIO;
312d0cad871SSteve Glendinning }
313d0cad871SSteve Glendinning
314d0cad871SSteve Glendinning return 0;
315d0cad871SSteve Glendinning }
316d0cad871SSteve Glendinning
smsc75xx_eeprom_confirm_not_busy(struct usbnet * dev)317d0cad871SSteve Glendinning static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev)
318d0cad871SSteve Glendinning {
319d0cad871SSteve Glendinning unsigned long start_time = jiffies;
320d0cad871SSteve Glendinning u32 val;
321d0cad871SSteve Glendinning int ret;
322d0cad871SSteve Glendinning
323d0cad871SSteve Glendinning do {
324d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, E2P_CMD, &val);
325e3c678e6SSteve Glendinning if (ret < 0) {
326e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading E2P_CMD\n");
327e3c678e6SSteve Glendinning return ret;
328e3c678e6SSteve Glendinning }
329d0cad871SSteve Glendinning
330d0cad871SSteve Glendinning if (!(val & E2P_CMD_BUSY))
331d0cad871SSteve Glendinning return 0;
332d0cad871SSteve Glendinning
333d0cad871SSteve Glendinning udelay(40);
334d0cad871SSteve Glendinning } while (!time_after(jiffies, start_time + HZ));
335d0cad871SSteve Glendinning
3361e1d7412SJoe Perches netdev_warn(dev->net, "EEPROM is busy\n");
337d0cad871SSteve Glendinning return -EIO;
338d0cad871SSteve Glendinning }
339d0cad871SSteve Glendinning
smsc75xx_read_eeprom(struct usbnet * dev,u32 offset,u32 length,u8 * data)340d0cad871SSteve Glendinning static int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length,
341d0cad871SSteve Glendinning u8 *data)
342d0cad871SSteve Glendinning {
343d0cad871SSteve Glendinning u32 val;
344d0cad871SSteve Glendinning int i, ret;
345d0cad871SSteve Glendinning
346d0cad871SSteve Glendinning BUG_ON(!dev);
347d0cad871SSteve Glendinning BUG_ON(!data);
348d0cad871SSteve Glendinning
349d0cad871SSteve Glendinning ret = smsc75xx_eeprom_confirm_not_busy(dev);
350d0cad871SSteve Glendinning if (ret)
351d0cad871SSteve Glendinning return ret;
352d0cad871SSteve Glendinning
353d0cad871SSteve Glendinning for (i = 0; i < length; i++) {
354d0cad871SSteve Glendinning val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR);
355d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, E2P_CMD, val);
356e3c678e6SSteve Glendinning if (ret < 0) {
357e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing E2P_CMD\n");
358e3c678e6SSteve Glendinning return ret;
359e3c678e6SSteve Glendinning }
360d0cad871SSteve Glendinning
361d0cad871SSteve Glendinning ret = smsc75xx_wait_eeprom(dev);
362d0cad871SSteve Glendinning if (ret < 0)
363d0cad871SSteve Glendinning return ret;
364d0cad871SSteve Glendinning
365d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, E2P_DATA, &val);
366e3c678e6SSteve Glendinning if (ret < 0) {
367e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading E2P_DATA\n");
368e3c678e6SSteve Glendinning return ret;
369e3c678e6SSteve Glendinning }
370d0cad871SSteve Glendinning
371d0cad871SSteve Glendinning data[i] = val & 0xFF;
372d0cad871SSteve Glendinning offset++;
373d0cad871SSteve Glendinning }
374d0cad871SSteve Glendinning
375d0cad871SSteve Glendinning return 0;
376d0cad871SSteve Glendinning }
377d0cad871SSteve Glendinning
smsc75xx_write_eeprom(struct usbnet * dev,u32 offset,u32 length,u8 * data)378d0cad871SSteve Glendinning static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length,
379d0cad871SSteve Glendinning u8 *data)
380d0cad871SSteve Glendinning {
381d0cad871SSteve Glendinning u32 val;
382d0cad871SSteve Glendinning int i, ret;
383d0cad871SSteve Glendinning
384d0cad871SSteve Glendinning BUG_ON(!dev);
385d0cad871SSteve Glendinning BUG_ON(!data);
386d0cad871SSteve Glendinning
387d0cad871SSteve Glendinning ret = smsc75xx_eeprom_confirm_not_busy(dev);
388d0cad871SSteve Glendinning if (ret)
389d0cad871SSteve Glendinning return ret;
390d0cad871SSteve Glendinning
391d0cad871SSteve Glendinning /* Issue write/erase enable command */
392d0cad871SSteve Glendinning val = E2P_CMD_BUSY | E2P_CMD_EWEN;
393d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, E2P_CMD, val);
394e3c678e6SSteve Glendinning if (ret < 0) {
395e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing E2P_CMD\n");
396e3c678e6SSteve Glendinning return ret;
397e3c678e6SSteve Glendinning }
398d0cad871SSteve Glendinning
399d0cad871SSteve Glendinning ret = smsc75xx_wait_eeprom(dev);
400d0cad871SSteve Glendinning if (ret < 0)
401d0cad871SSteve Glendinning return ret;
402d0cad871SSteve Glendinning
403d0cad871SSteve Glendinning for (i = 0; i < length; i++) {
404d0cad871SSteve Glendinning
405d0cad871SSteve Glendinning /* Fill data register */
406d0cad871SSteve Glendinning val = data[i];
407d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, E2P_DATA, val);
408e3c678e6SSteve Glendinning if (ret < 0) {
409e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing E2P_DATA\n");
410e3c678e6SSteve Glendinning return ret;
411e3c678e6SSteve Glendinning }
412d0cad871SSteve Glendinning
413d0cad871SSteve Glendinning /* Send "write" command */
414d0cad871SSteve Glendinning val = E2P_CMD_BUSY | E2P_CMD_WRITE | (offset & E2P_CMD_ADDR);
415d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, E2P_CMD, val);
416e3c678e6SSteve Glendinning if (ret < 0) {
417e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing E2P_CMD\n");
418e3c678e6SSteve Glendinning return ret;
419e3c678e6SSteve Glendinning }
420d0cad871SSteve Glendinning
421d0cad871SSteve Glendinning ret = smsc75xx_wait_eeprom(dev);
422d0cad871SSteve Glendinning if (ret < 0)
423d0cad871SSteve Glendinning return ret;
424d0cad871SSteve Glendinning
425d0cad871SSteve Glendinning offset++;
426d0cad871SSteve Glendinning }
427d0cad871SSteve Glendinning
428d0cad871SSteve Glendinning return 0;
429d0cad871SSteve Glendinning }
430d0cad871SSteve Glendinning
smsc75xx_dataport_wait_not_busy(struct usbnet * dev)431d0cad871SSteve Glendinning static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev)
432d0cad871SSteve Glendinning {
433d0cad871SSteve Glendinning int i, ret;
434d0cad871SSteve Glendinning
435d0cad871SSteve Glendinning for (i = 0; i < 100; i++) {
436d0cad871SSteve Glendinning u32 dp_sel;
437d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel);
438e3c678e6SSteve Glendinning if (ret < 0) {
439e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading DP_SEL\n");
440e3c678e6SSteve Glendinning return ret;
441e3c678e6SSteve Glendinning }
442d0cad871SSteve Glendinning
443d0cad871SSteve Glendinning if (dp_sel & DP_SEL_DPRDY)
444d0cad871SSteve Glendinning return 0;
445d0cad871SSteve Glendinning
446d0cad871SSteve Glendinning udelay(40);
447d0cad871SSteve Glendinning }
448d0cad871SSteve Glendinning
4491e1d7412SJoe Perches netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out\n");
450d0cad871SSteve Glendinning
451d0cad871SSteve Glendinning return -EIO;
452d0cad871SSteve Glendinning }
453d0cad871SSteve Glendinning
smsc75xx_dataport_write(struct usbnet * dev,u32 ram_select,u32 addr,u32 length,u32 * buf)454d0cad871SSteve Glendinning static int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr,
455d0cad871SSteve Glendinning u32 length, u32 *buf)
456d0cad871SSteve Glendinning {
457d0cad871SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
458d0cad871SSteve Glendinning u32 dp_sel;
459d0cad871SSteve Glendinning int i, ret;
460d0cad871SSteve Glendinning
461d0cad871SSteve Glendinning mutex_lock(&pdata->dataport_mutex);
462d0cad871SSteve Glendinning
463d0cad871SSteve Glendinning ret = smsc75xx_dataport_wait_not_busy(dev);
464e3c678e6SSteve Glendinning if (ret < 0) {
465e3c678e6SSteve Glendinning netdev_warn(dev->net, "smsc75xx_dataport_write busy on entry\n");
466e3c678e6SSteve Glendinning goto done;
467e3c678e6SSteve Glendinning }
468d0cad871SSteve Glendinning
469d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel);
470e3c678e6SSteve Glendinning if (ret < 0) {
471e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading DP_SEL\n");
472e3c678e6SSteve Glendinning goto done;
473e3c678e6SSteve Glendinning }
474d0cad871SSteve Glendinning
475d0cad871SSteve Glendinning dp_sel &= ~DP_SEL_RSEL;
476d0cad871SSteve Glendinning dp_sel |= ram_select;
477d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, DP_SEL, dp_sel);
478e3c678e6SSteve Glendinning if (ret < 0) {
479e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing DP_SEL\n");
480e3c678e6SSteve Glendinning goto done;
481e3c678e6SSteve Glendinning }
482d0cad871SSteve Glendinning
483d0cad871SSteve Glendinning for (i = 0; i < length; i++) {
484d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, DP_ADDR, addr + i);
485e3c678e6SSteve Glendinning if (ret < 0) {
486e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing DP_ADDR\n");
487e3c678e6SSteve Glendinning goto done;
488e3c678e6SSteve Glendinning }
489d0cad871SSteve Glendinning
490d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]);
491e3c678e6SSteve Glendinning if (ret < 0) {
492e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing DP_DATA\n");
493e3c678e6SSteve Glendinning goto done;
494e3c678e6SSteve Glendinning }
495d0cad871SSteve Glendinning
496d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE);
497e3c678e6SSteve Glendinning if (ret < 0) {
498e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing DP_CMD\n");
499e3c678e6SSteve Glendinning goto done;
500e3c678e6SSteve Glendinning }
501d0cad871SSteve Glendinning
502d0cad871SSteve Glendinning ret = smsc75xx_dataport_wait_not_busy(dev);
503e3c678e6SSteve Glendinning if (ret < 0) {
504e3c678e6SSteve Glendinning netdev_warn(dev->net, "smsc75xx_dataport_write timeout\n");
505e3c678e6SSteve Glendinning goto done;
506e3c678e6SSteve Glendinning }
507d0cad871SSteve Glendinning }
508d0cad871SSteve Glendinning
509d0cad871SSteve Glendinning done:
510d0cad871SSteve Glendinning mutex_unlock(&pdata->dataport_mutex);
511d0cad871SSteve Glendinning return ret;
512d0cad871SSteve Glendinning }
513d0cad871SSteve Glendinning
514d0cad871SSteve Glendinning /* returns hash bit number for given MAC address */
smsc75xx_hash(char addr[ETH_ALEN])515d0cad871SSteve Glendinning static u32 smsc75xx_hash(char addr[ETH_ALEN])
516d0cad871SSteve Glendinning {
517d0cad871SSteve Glendinning return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff;
518d0cad871SSteve Glendinning }
519d0cad871SSteve Glendinning
smsc75xx_deferred_multicast_write(struct work_struct * param)520d0cad871SSteve Glendinning static void smsc75xx_deferred_multicast_write(struct work_struct *param)
521d0cad871SSteve Glendinning {
522d0cad871SSteve Glendinning struct smsc75xx_priv *pdata =
523d0cad871SSteve Glendinning container_of(param, struct smsc75xx_priv, set_multicast);
524d0cad871SSteve Glendinning struct usbnet *dev = pdata->dev;
525d0cad871SSteve Glendinning int ret;
526d0cad871SSteve Glendinning
5271e1d7412SJoe Perches netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n",
528d0cad871SSteve Glendinning pdata->rfe_ctl);
529d0cad871SSteve Glendinning
530d0cad871SSteve Glendinning smsc75xx_dataport_write(dev, DP_SEL_VHF, DP_SEL_VHF_VLAN_LEN,
531d0cad871SSteve Glendinning DP_SEL_VHF_HASH_LEN, pdata->multicast_hash_table);
532d0cad871SSteve Glendinning
533d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
534e3c678e6SSteve Glendinning if (ret < 0)
535e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing RFE_CRL\n");
536d0cad871SSteve Glendinning }
537d0cad871SSteve Glendinning
smsc75xx_set_multicast(struct net_device * netdev)538d0cad871SSteve Glendinning static void smsc75xx_set_multicast(struct net_device *netdev)
539d0cad871SSteve Glendinning {
540d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
541d0cad871SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
542d0cad871SSteve Glendinning unsigned long flags;
543d0cad871SSteve Glendinning int i;
544d0cad871SSteve Glendinning
545d0cad871SSteve Glendinning spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
546d0cad871SSteve Glendinning
547d0cad871SSteve Glendinning pdata->rfe_ctl &=
548d0cad871SSteve Glendinning ~(RFE_CTL_AU | RFE_CTL_AM | RFE_CTL_DPF | RFE_CTL_MHF);
549d0cad871SSteve Glendinning pdata->rfe_ctl |= RFE_CTL_AB;
550d0cad871SSteve Glendinning
551d0cad871SSteve Glendinning for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++)
552d0cad871SSteve Glendinning pdata->multicast_hash_table[i] = 0;
553d0cad871SSteve Glendinning
554d0cad871SSteve Glendinning if (dev->net->flags & IFF_PROMISC) {
5551e1d7412SJoe Perches netif_dbg(dev, drv, dev->net, "promiscuous mode enabled\n");
556d0cad871SSteve Glendinning pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_AU;
557d0cad871SSteve Glendinning } else if (dev->net->flags & IFF_ALLMULTI) {
5581e1d7412SJoe Perches netif_dbg(dev, drv, dev->net, "receive all multicast enabled\n");
559d0cad871SSteve Glendinning pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_DPF;
560d0cad871SSteve Glendinning } else if (!netdev_mc_empty(dev->net)) {
56122bedad3SJiri Pirko struct netdev_hw_addr *ha;
562d0cad871SSteve Glendinning
5631e1d7412SJoe Perches netif_dbg(dev, drv, dev->net, "receive multicast hash filter\n");
564d0cad871SSteve Glendinning
565d0cad871SSteve Glendinning pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF;
566d0cad871SSteve Glendinning
56722bedad3SJiri Pirko netdev_for_each_mc_addr(ha, netdev) {
56822bedad3SJiri Pirko u32 bitnum = smsc75xx_hash(ha->addr);
569d0cad871SSteve Glendinning pdata->multicast_hash_table[bitnum / 32] |=
570d0cad871SSteve Glendinning (1 << (bitnum % 32));
571d0cad871SSteve Glendinning }
572d0cad871SSteve Glendinning } else {
5731e1d7412SJoe Perches netif_dbg(dev, drv, dev->net, "receive own packets only\n");
574d0cad871SSteve Glendinning pdata->rfe_ctl |= RFE_CTL_DPF;
575d0cad871SSteve Glendinning }
576d0cad871SSteve Glendinning
577d0cad871SSteve Glendinning spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
578d0cad871SSteve Glendinning
579d0cad871SSteve Glendinning /* defer register writes to a sleepable context */
580d0cad871SSteve Glendinning schedule_work(&pdata->set_multicast);
581d0cad871SSteve Glendinning }
582d0cad871SSteve Glendinning
smsc75xx_update_flowcontrol(struct usbnet * dev,u8 duplex,u16 lcladv,u16 rmtadv)583d0cad871SSteve Glendinning static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex,
584d0cad871SSteve Glendinning u16 lcladv, u16 rmtadv)
585d0cad871SSteve Glendinning {
586d0cad871SSteve Glendinning u32 flow = 0, fct_flow = 0;
587d0cad871SSteve Glendinning int ret;
588d0cad871SSteve Glendinning
589d0cad871SSteve Glendinning if (duplex == DUPLEX_FULL) {
590d0cad871SSteve Glendinning u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
591d0cad871SSteve Glendinning
592d0cad871SSteve Glendinning if (cap & FLOW_CTRL_TX) {
593d0cad871SSteve Glendinning flow = (FLOW_TX_FCEN | 0xFFFF);
594d0cad871SSteve Glendinning /* set fct_flow thresholds to 20% and 80% */
595d0cad871SSteve Glendinning fct_flow = (8 << 8) | 32;
596d0cad871SSteve Glendinning }
597d0cad871SSteve Glendinning
598d0cad871SSteve Glendinning if (cap & FLOW_CTRL_RX)
599d0cad871SSteve Glendinning flow |= FLOW_RX_FCEN;
600d0cad871SSteve Glendinning
6011e1d7412SJoe Perches netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n",
602d0cad871SSteve Glendinning (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
603d0cad871SSteve Glendinning (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
604d0cad871SSteve Glendinning } else {
6051e1d7412SJoe Perches netif_dbg(dev, link, dev->net, "half duplex\n");
606d0cad871SSteve Glendinning }
607d0cad871SSteve Glendinning
608d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FLOW, flow);
609e3c678e6SSteve Glendinning if (ret < 0) {
610e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing FLOW\n");
611e3c678e6SSteve Glendinning return ret;
612e3c678e6SSteve Glendinning }
613d0cad871SSteve Glendinning
614d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow);
615e3c678e6SSteve Glendinning if (ret < 0) {
616e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing FCT_FLOW\n");
617e3c678e6SSteve Glendinning return ret;
618e3c678e6SSteve Glendinning }
619d0cad871SSteve Glendinning
620d0cad871SSteve Glendinning return 0;
621d0cad871SSteve Glendinning }
622d0cad871SSteve Glendinning
smsc75xx_link_reset(struct usbnet * dev)623d0cad871SSteve Glendinning static int smsc75xx_link_reset(struct usbnet *dev)
624d0cad871SSteve Glendinning {
625d0cad871SSteve Glendinning struct mii_if_info *mii = &dev->mii;
6268ae6dacaSDavid Decotigny struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
627d0cad871SSteve Glendinning u16 lcladv, rmtadv;
628d0cad871SSteve Glendinning int ret;
629d0cad871SSteve Glendinning
6304f94a929SSteve Glendinning /* write to clear phy interrupt status */
6317749622dSSteve Glendinning smsc75xx_mdio_write(dev->net, mii->phy_id, PHY_INT_SRC,
6327749622dSSteve Glendinning PHY_INT_SRC_CLEAR_ALL);
633d0cad871SSteve Glendinning
634d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL);
635e3c678e6SSteve Glendinning if (ret < 0) {
636e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing INT_STS\n");
637e3c678e6SSteve Glendinning return ret;
638e3c678e6SSteve Glendinning }
639d0cad871SSteve Glendinning
640d0cad871SSteve Glendinning mii_check_media(mii, 1, 1);
641d0cad871SSteve Glendinning mii_ethtool_gset(&dev->mii, &ecmd);
642d0cad871SSteve Glendinning lcladv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE);
643d0cad871SSteve Glendinning rmtadv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_LPA);
644d0cad871SSteve Glendinning
6451e1d7412SJoe Perches netif_dbg(dev, link, dev->net, "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n",
6461e1d7412SJoe Perches ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv);
647d0cad871SSteve Glendinning
648d0cad871SSteve Glendinning return smsc75xx_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv);
649d0cad871SSteve Glendinning }
650d0cad871SSteve Glendinning
smsc75xx_status(struct usbnet * dev,struct urb * urb)651d0cad871SSteve Glendinning static void smsc75xx_status(struct usbnet *dev, struct urb *urb)
652d0cad871SSteve Glendinning {
653d0cad871SSteve Glendinning u32 intdata;
654d0cad871SSteve Glendinning
655d0cad871SSteve Glendinning if (urb->actual_length != 4) {
6561e1d7412SJoe Perches netdev_warn(dev->net, "unexpected urb length %d\n",
6571e1d7412SJoe Perches urb->actual_length);
658d0cad871SSteve Glendinning return;
659d0cad871SSteve Glendinning }
660d0cad871SSteve Glendinning
6615864118bSChuhong Yuan intdata = get_unaligned_le32(urb->transfer_buffer);
662d0cad871SSteve Glendinning
6631e1d7412SJoe Perches netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata);
664d0cad871SSteve Glendinning
665d0cad871SSteve Glendinning if (intdata & INT_ENP_PHY_INT)
666d0cad871SSteve Glendinning usbnet_defer_kevent(dev, EVENT_LINK_RESET);
667d0cad871SSteve Glendinning else
6681e1d7412SJoe Perches netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n",
6691e1d7412SJoe Perches intdata);
670d0cad871SSteve Glendinning }
671d0cad871SSteve Glendinning
smsc75xx_ethtool_get_eeprom_len(struct net_device * net)672d0cad871SSteve Glendinning static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net)
673d0cad871SSteve Glendinning {
674d0cad871SSteve Glendinning return MAX_EEPROM_SIZE;
675d0cad871SSteve Glendinning }
676d0cad871SSteve Glendinning
smsc75xx_ethtool_get_eeprom(struct net_device * netdev,struct ethtool_eeprom * ee,u8 * data)677d0cad871SSteve Glendinning static int smsc75xx_ethtool_get_eeprom(struct net_device *netdev,
678d0cad871SSteve Glendinning struct ethtool_eeprom *ee, u8 *data)
679d0cad871SSteve Glendinning {
680d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
681d0cad871SSteve Glendinning
682d0cad871SSteve Glendinning ee->magic = LAN75XX_EEPROM_MAGIC;
683d0cad871SSteve Glendinning
684d0cad871SSteve Glendinning return smsc75xx_read_eeprom(dev, ee->offset, ee->len, data);
685d0cad871SSteve Glendinning }
686d0cad871SSteve Glendinning
smsc75xx_ethtool_set_eeprom(struct net_device * netdev,struct ethtool_eeprom * ee,u8 * data)687d0cad871SSteve Glendinning static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev,
688d0cad871SSteve Glendinning struct ethtool_eeprom *ee, u8 *data)
689d0cad871SSteve Glendinning {
690d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
691d0cad871SSteve Glendinning
692d0cad871SSteve Glendinning if (ee->magic != LAN75XX_EEPROM_MAGIC) {
6931e1d7412SJoe Perches netdev_warn(dev->net, "EEPROM: magic value mismatch: 0x%x\n",
6941e1d7412SJoe Perches ee->magic);
695d0cad871SSteve Glendinning return -EINVAL;
696d0cad871SSteve Glendinning }
697d0cad871SSteve Glendinning
698d0cad871SSteve Glendinning return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data);
699d0cad871SSteve Glendinning }
700d0cad871SSteve Glendinning
smsc75xx_ethtool_get_wol(struct net_device * net,struct ethtool_wolinfo * wolinfo)7016c636503SSteve Glendinning static void smsc75xx_ethtool_get_wol(struct net_device *net,
7026c636503SSteve Glendinning struct ethtool_wolinfo *wolinfo)
7036c636503SSteve Glendinning {
7046c636503SSteve Glendinning struct usbnet *dev = netdev_priv(net);
7056c636503SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
7066c636503SSteve Glendinning
7076c636503SSteve Glendinning wolinfo->supported = SUPPORTED_WAKE;
7086c636503SSteve Glendinning wolinfo->wolopts = pdata->wolopts;
7096c636503SSteve Glendinning }
7106c636503SSteve Glendinning
smsc75xx_ethtool_set_wol(struct net_device * net,struct ethtool_wolinfo * wolinfo)7116c636503SSteve Glendinning static int smsc75xx_ethtool_set_wol(struct net_device *net,
7126c636503SSteve Glendinning struct ethtool_wolinfo *wolinfo)
7136c636503SSteve Glendinning {
7146c636503SSteve Glendinning struct usbnet *dev = netdev_priv(net);
7156c636503SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
716351f33d9SSteve Glendinning int ret;
7176c636503SSteve Glendinning
7189c734b27SFlorian Fainelli if (wolinfo->wolopts & ~SUPPORTED_WAKE)
7199c734b27SFlorian Fainelli return -EINVAL;
7209c734b27SFlorian Fainelli
7216c636503SSteve Glendinning pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE;
722351f33d9SSteve Glendinning
723351f33d9SSteve Glendinning ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts);
724e3c678e6SSteve Glendinning if (ret < 0)
725e3c678e6SSteve Glendinning netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret);
726351f33d9SSteve Glendinning
727e3c678e6SSteve Glendinning return ret;
7286c636503SSteve Glendinning }
7296c636503SSteve Glendinning
730d0cad871SSteve Glendinning static const struct ethtool_ops smsc75xx_ethtool_ops = {
731d0cad871SSteve Glendinning .get_link = usbnet_get_link,
732d0cad871SSteve Glendinning .nway_reset = usbnet_nway_reset,
733d0cad871SSteve Glendinning .get_drvinfo = usbnet_get_drvinfo,
734d0cad871SSteve Glendinning .get_msglevel = usbnet_get_msglevel,
735d0cad871SSteve Glendinning .set_msglevel = usbnet_set_msglevel,
736d0cad871SSteve Glendinning .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len,
737d0cad871SSteve Glendinning .get_eeprom = smsc75xx_ethtool_get_eeprom,
738d0cad871SSteve Glendinning .set_eeprom = smsc75xx_ethtool_set_eeprom,
7396c636503SSteve Glendinning .get_wol = smsc75xx_ethtool_get_wol,
7406c636503SSteve Glendinning .set_wol = smsc75xx_ethtool_set_wol,
74177651900SOliver Neukum .get_link_ksettings = usbnet_get_link_ksettings_mii,
74277651900SOliver Neukum .set_link_ksettings = usbnet_set_link_ksettings_mii,
743d0cad871SSteve Glendinning };
744d0cad871SSteve Glendinning
smsc75xx_ioctl(struct net_device * netdev,struct ifreq * rq,int cmd)745d0cad871SSteve Glendinning static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
746d0cad871SSteve Glendinning {
747d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
748d0cad871SSteve Glendinning
749d0cad871SSteve Glendinning if (!netif_running(netdev))
750d0cad871SSteve Glendinning return -EINVAL;
751d0cad871SSteve Glendinning
752d0cad871SSteve Glendinning return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
753d0cad871SSteve Glendinning }
754d0cad871SSteve Glendinning
smsc75xx_init_mac_address(struct usbnet * dev)755d0cad871SSteve Glendinning static void smsc75xx_init_mac_address(struct usbnet *dev)
756d0cad871SSteve Glendinning {
757a7021af7SJakub Kicinski u8 addr[ETH_ALEN];
758a7021af7SJakub Kicinski
759c489565bSArnd Bergmann /* maybe the boot loader passed the MAC address in devicetree */
7604d04cdc5SJakub Kicinski if (!platform_get_ethdev_address(&dev->udev->dev, dev->net)) {
7614f359b65SŁukasz Stelmach if (is_valid_ether_addr(dev->net->dev_addr)) {
7624f359b65SŁukasz Stelmach /* device tree values are valid so use them */
7634f359b65SŁukasz Stelmach netif_dbg(dev, ifup, dev->net, "MAC address read from the device tree\n");
764c489565bSArnd Bergmann return;
765c489565bSArnd Bergmann }
7664f359b65SŁukasz Stelmach }
767c489565bSArnd Bergmann
768d0cad871SSteve Glendinning /* try reading mac address from EEPROM */
769a7021af7SJakub Kicinski if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, addr) == 0) {
770a7021af7SJakub Kicinski eth_hw_addr_set(dev->net, addr);
771d0cad871SSteve Glendinning if (is_valid_ether_addr(dev->net->dev_addr)) {
772d0cad871SSteve Glendinning /* eeprom values are valid so use them */
773d0cad871SSteve Glendinning netif_dbg(dev, ifup, dev->net,
7741e1d7412SJoe Perches "MAC address read from EEPROM\n");
775d0cad871SSteve Glendinning return;
776d0cad871SSteve Glendinning }
777d0cad871SSteve Glendinning }
778d0cad871SSteve Glendinning
779c489565bSArnd Bergmann /* no useful static MAC address found. generate a random one */
780f2cedb63SDanny Kukawka eth_hw_addr_random(dev->net);
7811e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n");
782d0cad871SSteve Glendinning }
783d0cad871SSteve Glendinning
smsc75xx_set_mac_address(struct usbnet * dev)784d0cad871SSteve Glendinning static int smsc75xx_set_mac_address(struct usbnet *dev)
785d0cad871SSteve Glendinning {
786d0cad871SSteve Glendinning u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 |
787d0cad871SSteve Glendinning dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24;
788d0cad871SSteve Glendinning u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8;
789d0cad871SSteve Glendinning
790d0cad871SSteve Glendinning int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi);
791e3c678e6SSteve Glendinning if (ret < 0) {
792e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write RX_ADDRH: %d\n", ret);
793e3c678e6SSteve Glendinning return ret;
794e3c678e6SSteve Glendinning }
795d0cad871SSteve Glendinning
796d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, RX_ADDRL, addr_lo);
797e3c678e6SSteve Glendinning if (ret < 0) {
798e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write RX_ADDRL: %d\n", ret);
799e3c678e6SSteve Glendinning return ret;
800e3c678e6SSteve Glendinning }
801d0cad871SSteve Glendinning
802d0cad871SSteve Glendinning addr_hi |= ADDR_FILTX_FB_VALID;
803d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, ADDR_FILTX, addr_hi);
804e3c678e6SSteve Glendinning if (ret < 0) {
805e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write ADDR_FILTX: %d\n", ret);
806e3c678e6SSteve Glendinning return ret;
807e3c678e6SSteve Glendinning }
808d0cad871SSteve Glendinning
809d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, ADDR_FILTX + 4, addr_lo);
810e3c678e6SSteve Glendinning if (ret < 0)
811e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write ADDR_FILTX+4: %d\n", ret);
812d0cad871SSteve Glendinning
813e3c678e6SSteve Glendinning return ret;
814d0cad871SSteve Glendinning }
815d0cad871SSteve Glendinning
smsc75xx_phy_initialize(struct usbnet * dev)816d0cad871SSteve Glendinning static int smsc75xx_phy_initialize(struct usbnet *dev)
817d0cad871SSteve Glendinning {
818b140504aSSteve Glendinning int bmcr, ret, timeout = 0;
819d0cad871SSteve Glendinning
820d0cad871SSteve Glendinning /* Initialize MII structure */
821d0cad871SSteve Glendinning dev->mii.dev = dev->net;
822d0cad871SSteve Glendinning dev->mii.mdio_read = smsc75xx_mdio_read;
823d0cad871SSteve Glendinning dev->mii.mdio_write = smsc75xx_mdio_write;
824d0cad871SSteve Glendinning dev->mii.phy_id_mask = 0x1f;
825d0cad871SSteve Glendinning dev->mii.reg_num_mask = 0x1f;
826c0b92e4dSSteve Glendinning dev->mii.supports_gmii = 1;
827d0cad871SSteve Glendinning dev->mii.phy_id = SMSC75XX_INTERNAL_PHY_ID;
828d0cad871SSteve Glendinning
829d0cad871SSteve Glendinning /* reset phy and wait for reset to complete */
830d0cad871SSteve Glendinning smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
831d0cad871SSteve Glendinning
832d0cad871SSteve Glendinning do {
833d0cad871SSteve Glendinning msleep(10);
834d0cad871SSteve Glendinning bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR);
835e3c678e6SSteve Glendinning if (bmcr < 0) {
836e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading MII_BMCR\n");
837e3c678e6SSteve Glendinning return bmcr;
838e3c678e6SSteve Glendinning }
839d0cad871SSteve Glendinning timeout++;
8408a1d59d7SSteve Glendinning } while ((bmcr & BMCR_RESET) && (timeout < 100));
841d0cad871SSteve Glendinning
842d0cad871SSteve Glendinning if (timeout >= 100) {
8431e1d7412SJoe Perches netdev_warn(dev->net, "timeout on PHY Reset\n");
844d0cad871SSteve Glendinning return -EIO;
845d0cad871SSteve Glendinning }
846d0cad871SSteve Glendinning
847d461e3daSYuiko Oshino /* phy workaround for gig link */
848d461e3daSYuiko Oshino smsc75xx_phy_gig_workaround(dev);
849d461e3daSYuiko Oshino
850d0cad871SSteve Glendinning smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
851d0cad871SSteve Glendinning ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
852d0cad871SSteve Glendinning ADVERTISE_PAUSE_ASYM);
853c0b92e4dSSteve Glendinning smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
854c0b92e4dSSteve Glendinning ADVERTISE_1000FULL);
855d0cad871SSteve Glendinning
856b140504aSSteve Glendinning /* read and write to clear phy interrupt status */
857b140504aSSteve Glendinning ret = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC);
858e3c678e6SSteve Glendinning if (ret < 0) {
859e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PHY_INT_SRC\n");
860e3c678e6SSteve Glendinning return ret;
861e3c678e6SSteve Glendinning }
862e3c678e6SSteve Glendinning
863b140504aSSteve Glendinning smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_SRC, 0xffff);
864d0cad871SSteve Glendinning
865d0cad871SSteve Glendinning smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK,
866d0cad871SSteve Glendinning PHY_INT_MASK_DEFAULT);
867d0cad871SSteve Glendinning mii_nway_restart(&dev->mii);
868d0cad871SSteve Glendinning
8691e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n");
870d0cad871SSteve Glendinning return 0;
871d0cad871SSteve Glendinning }
872d0cad871SSteve Glendinning
smsc75xx_set_rx_max_frame_length(struct usbnet * dev,int size)873d0cad871SSteve Glendinning static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size)
874d0cad871SSteve Glendinning {
875d0cad871SSteve Glendinning int ret = 0;
876d0cad871SSteve Glendinning u32 buf;
877d0cad871SSteve Glendinning bool rxenabled;
878d0cad871SSteve Glendinning
879d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, MAC_RX, &buf);
880e3c678e6SSteve Glendinning if (ret < 0) {
881e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret);
882e3c678e6SSteve Glendinning return ret;
883e3c678e6SSteve Glendinning }
884d0cad871SSteve Glendinning
885d0cad871SSteve Glendinning rxenabled = ((buf & MAC_RX_RXEN) != 0);
886d0cad871SSteve Glendinning
887d0cad871SSteve Glendinning if (rxenabled) {
888d0cad871SSteve Glendinning buf &= ~MAC_RX_RXEN;
889d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, MAC_RX, buf);
890e3c678e6SSteve Glendinning if (ret < 0) {
891e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret);
892e3c678e6SSteve Glendinning return ret;
893e3c678e6SSteve Glendinning }
894d0cad871SSteve Glendinning }
895d0cad871SSteve Glendinning
896d0cad871SSteve Glendinning /* add 4 to size for FCS */
897d0cad871SSteve Glendinning buf &= ~MAC_RX_MAX_SIZE;
898d0cad871SSteve Glendinning buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE);
899d0cad871SSteve Glendinning
900d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, MAC_RX, buf);
901e3c678e6SSteve Glendinning if (ret < 0) {
902e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret);
903e3c678e6SSteve Glendinning return ret;
904e3c678e6SSteve Glendinning }
905d0cad871SSteve Glendinning
906d0cad871SSteve Glendinning if (rxenabled) {
907d0cad871SSteve Glendinning buf |= MAC_RX_RXEN;
908d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, MAC_RX, buf);
909e3c678e6SSteve Glendinning if (ret < 0) {
910e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret);
911e3c678e6SSteve Glendinning return ret;
912e3c678e6SSteve Glendinning }
913d0cad871SSteve Glendinning }
914d0cad871SSteve Glendinning
915d0cad871SSteve Glendinning return 0;
916d0cad871SSteve Glendinning }
917d0cad871SSteve Glendinning
smsc75xx_change_mtu(struct net_device * netdev,int new_mtu)918d0cad871SSteve Glendinning static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu)
919d0cad871SSteve Glendinning {
920d0cad871SSteve Glendinning struct usbnet *dev = netdev_priv(netdev);
9214c51e536SSteve Glendinning int ret;
922d0cad871SSteve Glendinning
9234c51e536SSteve Glendinning ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu + ETH_HLEN);
924e3c678e6SSteve Glendinning if (ret < 0) {
925e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to set mac rx frame length\n");
926e3c678e6SSteve Glendinning return ret;
927e3c678e6SSteve Glendinning }
928d0cad871SSteve Glendinning
929d0cad871SSteve Glendinning return usbnet_change_mtu(netdev, new_mtu);
930d0cad871SSteve Glendinning }
931d0cad871SSteve Glendinning
93278e47fe4SMichał Mirosław /* Enable or disable Rx checksum offload engine */
smsc75xx_set_features(struct net_device * netdev,netdev_features_t features)933c8f44affSMichał Mirosław static int smsc75xx_set_features(struct net_device *netdev,
934c8f44affSMichał Mirosław netdev_features_t features)
93578e47fe4SMichał Mirosław {
93678e47fe4SMichał Mirosław struct usbnet *dev = netdev_priv(netdev);
93778e47fe4SMichał Mirosław struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
93878e47fe4SMichał Mirosław unsigned long flags;
93978e47fe4SMichał Mirosław int ret;
94078e47fe4SMichał Mirosław
94178e47fe4SMichał Mirosław spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
94278e47fe4SMichał Mirosław
94378e47fe4SMichał Mirosław if (features & NETIF_F_RXCSUM)
94478e47fe4SMichał Mirosław pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM;
94578e47fe4SMichał Mirosław else
94678e47fe4SMichał Mirosław pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM);
94778e47fe4SMichał Mirosław
94878e47fe4SMichał Mirosław spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
94978e47fe4SMichał Mirosław /* it's racing here! */
95078e47fe4SMichał Mirosław
95178e47fe4SMichał Mirosław ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
95288e80c62SEric Dumazet if (ret < 0) {
953e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing RFE_CTL\n");
954e3c678e6SSteve Glendinning return ret;
95578e47fe4SMichał Mirosław }
95688e80c62SEric Dumazet return 0;
95788e80c62SEric Dumazet }
95878e47fe4SMichał Mirosław
smsc75xx_wait_ready(struct usbnet * dev,int in_pm)95947bbea41SMing Lei static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm)
9608762cec8SSteve Glendinning {
9618762cec8SSteve Glendinning int timeout = 0;
9628762cec8SSteve Glendinning
9638762cec8SSteve Glendinning do {
9648762cec8SSteve Glendinning u32 buf;
96547bbea41SMing Lei int ret;
96647bbea41SMing Lei
96747bbea41SMing Lei ret = __smsc75xx_read_reg(dev, PMT_CTL, &buf, in_pm);
96847bbea41SMing Lei
969e3c678e6SSteve Glendinning if (ret < 0) {
970e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret);
971e3c678e6SSteve Glendinning return ret;
972e3c678e6SSteve Glendinning }
9738762cec8SSteve Glendinning
9748762cec8SSteve Glendinning if (buf & PMT_CTL_DEV_RDY)
9758762cec8SSteve Glendinning return 0;
9768762cec8SSteve Glendinning
9778762cec8SSteve Glendinning msleep(10);
9788762cec8SSteve Glendinning timeout++;
9798762cec8SSteve Glendinning } while (timeout < 100);
9808762cec8SSteve Glendinning
9811e1d7412SJoe Perches netdev_warn(dev->net, "timeout waiting for device ready\n");
9828762cec8SSteve Glendinning return -EIO;
9838762cec8SSteve Glendinning }
9848762cec8SSteve Glendinning
smsc75xx_phy_gig_workaround(struct usbnet * dev)985d461e3daSYuiko Oshino static int smsc75xx_phy_gig_workaround(struct usbnet *dev)
986d461e3daSYuiko Oshino {
987d461e3daSYuiko Oshino struct mii_if_info *mii = &dev->mii;
988d461e3daSYuiko Oshino int ret = 0, timeout = 0;
989d461e3daSYuiko Oshino u32 buf, link_up = 0;
990d461e3daSYuiko Oshino
991d461e3daSYuiko Oshino /* Set the phy in Gig loopback */
992d461e3daSYuiko Oshino smsc75xx_mdio_write(dev->net, mii->phy_id, MII_BMCR, 0x4040);
993d461e3daSYuiko Oshino
994d461e3daSYuiko Oshino /* Wait for the link up */
995d461e3daSYuiko Oshino do {
996d461e3daSYuiko Oshino link_up = smsc75xx_link_ok_nopm(dev);
997d461e3daSYuiko Oshino usleep_range(10000, 20000);
998d461e3daSYuiko Oshino timeout++;
999d461e3daSYuiko Oshino } while ((!link_up) && (timeout < 1000));
1000d461e3daSYuiko Oshino
1001d461e3daSYuiko Oshino if (timeout >= 1000) {
1002d461e3daSYuiko Oshino netdev_warn(dev->net, "Timeout waiting for PHY link up\n");
1003d461e3daSYuiko Oshino return -EIO;
1004d461e3daSYuiko Oshino }
1005d461e3daSYuiko Oshino
1006d461e3daSYuiko Oshino /* phy reset */
1007d461e3daSYuiko Oshino ret = smsc75xx_read_reg(dev, PMT_CTL, &buf);
1008d461e3daSYuiko Oshino if (ret < 0) {
1009d461e3daSYuiko Oshino netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret);
1010d461e3daSYuiko Oshino return ret;
1011d461e3daSYuiko Oshino }
1012d461e3daSYuiko Oshino
1013d461e3daSYuiko Oshino buf |= PMT_CTL_PHY_RST;
1014d461e3daSYuiko Oshino
1015d461e3daSYuiko Oshino ret = smsc75xx_write_reg(dev, PMT_CTL, buf);
1016d461e3daSYuiko Oshino if (ret < 0) {
1017d461e3daSYuiko Oshino netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret);
1018d461e3daSYuiko Oshino return ret;
1019d461e3daSYuiko Oshino }
1020d461e3daSYuiko Oshino
1021d461e3daSYuiko Oshino timeout = 0;
1022d461e3daSYuiko Oshino do {
1023d461e3daSYuiko Oshino usleep_range(10000, 20000);
1024d461e3daSYuiko Oshino ret = smsc75xx_read_reg(dev, PMT_CTL, &buf);
1025d461e3daSYuiko Oshino if (ret < 0) {
1026d461e3daSYuiko Oshino netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n",
1027d461e3daSYuiko Oshino ret);
1028d461e3daSYuiko Oshino return ret;
1029d461e3daSYuiko Oshino }
1030d461e3daSYuiko Oshino timeout++;
1031d461e3daSYuiko Oshino } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100));
1032d461e3daSYuiko Oshino
1033d461e3daSYuiko Oshino if (timeout >= 100) {
1034d461e3daSYuiko Oshino netdev_warn(dev->net, "timeout waiting for PHY Reset\n");
1035d461e3daSYuiko Oshino return -EIO;
1036d461e3daSYuiko Oshino }
1037d461e3daSYuiko Oshino
1038d461e3daSYuiko Oshino return 0;
1039d461e3daSYuiko Oshino }
1040d461e3daSYuiko Oshino
smsc75xx_reset(struct usbnet * dev)1041d0cad871SSteve Glendinning static int smsc75xx_reset(struct usbnet *dev)
1042d0cad871SSteve Glendinning {
1043d0cad871SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1044d0cad871SSteve Glendinning u32 buf;
1045d0cad871SSteve Glendinning int ret = 0, timeout;
1046d0cad871SSteve Glendinning
10471e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset\n");
1048d0cad871SSteve Glendinning
104947bbea41SMing Lei ret = smsc75xx_wait_ready(dev, 0);
1050e3c678e6SSteve Glendinning if (ret < 0) {
1051e3c678e6SSteve Glendinning netdev_warn(dev->net, "device not ready in smsc75xx_reset\n");
1052e3c678e6SSteve Glendinning return ret;
1053e3c678e6SSteve Glendinning }
10548762cec8SSteve Glendinning
1055d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
1056e3c678e6SSteve Glendinning if (ret < 0) {
1057e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret);
1058e3c678e6SSteve Glendinning return ret;
1059e3c678e6SSteve Glendinning }
1060d0cad871SSteve Glendinning
1061d0cad871SSteve Glendinning buf |= HW_CFG_LRST;
1062d0cad871SSteve Glendinning
1063d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, HW_CFG, buf);
1064e3c678e6SSteve Glendinning if (ret < 0) {
1065e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret);
1066e3c678e6SSteve Glendinning return ret;
1067e3c678e6SSteve Glendinning }
1068d0cad871SSteve Glendinning
1069d0cad871SSteve Glendinning timeout = 0;
1070d0cad871SSteve Glendinning do {
1071d0cad871SSteve Glendinning msleep(10);
1072d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
1073e3c678e6SSteve Glendinning if (ret < 0) {
1074e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret);
1075e3c678e6SSteve Glendinning return ret;
1076e3c678e6SSteve Glendinning }
1077d0cad871SSteve Glendinning timeout++;
1078d0cad871SSteve Glendinning } while ((buf & HW_CFG_LRST) && (timeout < 100));
1079d0cad871SSteve Glendinning
1080d0cad871SSteve Glendinning if (timeout >= 100) {
10811e1d7412SJoe Perches netdev_warn(dev->net, "timeout on completion of Lite Reset\n");
1082d0cad871SSteve Glendinning return -EIO;
1083d0cad871SSteve Glendinning }
1084d0cad871SSteve Glendinning
10851e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY\n");
1086d0cad871SSteve Glendinning
1087d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, PMT_CTL, &buf);
1088e3c678e6SSteve Glendinning if (ret < 0) {
1089e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret);
1090e3c678e6SSteve Glendinning return ret;
1091e3c678e6SSteve Glendinning }
1092d0cad871SSteve Glendinning
1093d0cad871SSteve Glendinning buf |= PMT_CTL_PHY_RST;
1094d0cad871SSteve Glendinning
1095d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, PMT_CTL, buf);
1096e3c678e6SSteve Glendinning if (ret < 0) {
1097e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret);
1098e3c678e6SSteve Glendinning return ret;
1099e3c678e6SSteve Glendinning }
1100d0cad871SSteve Glendinning
1101d0cad871SSteve Glendinning timeout = 0;
1102d0cad871SSteve Glendinning do {
1103d0cad871SSteve Glendinning msleep(10);
1104d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, PMT_CTL, &buf);
1105e3c678e6SSteve Glendinning if (ret < 0) {
1106e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret);
1107e3c678e6SSteve Glendinning return ret;
1108e3c678e6SSteve Glendinning }
1109d0cad871SSteve Glendinning timeout++;
1110d0cad871SSteve Glendinning } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100));
1111d0cad871SSteve Glendinning
1112d0cad871SSteve Glendinning if (timeout >= 100) {
11131e1d7412SJoe Perches netdev_warn(dev->net, "timeout waiting for PHY Reset\n");
1114d0cad871SSteve Glendinning return -EIO;
1115d0cad871SSteve Glendinning }
1116d0cad871SSteve Glendinning
11171e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "PHY reset complete\n");
1118d0cad871SSteve Glendinning
1119d0cad871SSteve Glendinning ret = smsc75xx_set_mac_address(dev);
1120e3c678e6SSteve Glendinning if (ret < 0) {
1121e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to set mac address\n");
1122e3c678e6SSteve Glendinning return ret;
1123e3c678e6SSteve Glendinning }
1124d0cad871SSteve Glendinning
11251e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n",
11261e1d7412SJoe Perches dev->net->dev_addr);
1127d0cad871SSteve Glendinning
1128d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
1129e3c678e6SSteve Glendinning if (ret < 0) {
1130e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret);
1131e3c678e6SSteve Glendinning return ret;
1132e3c678e6SSteve Glendinning }
1133d0cad871SSteve Glendinning
11341e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n",
11351e1d7412SJoe Perches buf);
1136d0cad871SSteve Glendinning
1137d0cad871SSteve Glendinning buf |= HW_CFG_BIR;
1138d0cad871SSteve Glendinning
1139d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, HW_CFG, buf);
1140e3c678e6SSteve Glendinning if (ret < 0) {
1141e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret);
1142e3c678e6SSteve Glendinning return ret;
1143e3c678e6SSteve Glendinning }
1144d0cad871SSteve Glendinning
1145d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
1146e3c678e6SSteve Glendinning if (ret < 0) {
1147e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret);
1148e3c678e6SSteve Glendinning return ret;
1149e3c678e6SSteve Glendinning }
1150d0cad871SSteve Glendinning
11511e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing HW_CFG_BIR: 0x%08x\n",
11521e1d7412SJoe Perches buf);
1153d0cad871SSteve Glendinning
1154d0cad871SSteve Glendinning if (!turbo_mode) {
1155d0cad871SSteve Glendinning buf = 0;
1156d0cad871SSteve Glendinning dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
1157d0cad871SSteve Glendinning } else if (dev->udev->speed == USB_SPEED_HIGH) {
1158d0cad871SSteve Glendinning buf = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
1159d0cad871SSteve Glendinning dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
1160d0cad871SSteve Glendinning } else {
1161d0cad871SSteve Glendinning buf = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
1162d0cad871SSteve Glendinning dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
1163d0cad871SSteve Glendinning }
1164d0cad871SSteve Glendinning
11651e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n",
1166d0cad871SSteve Glendinning (ulong)dev->rx_urb_size);
1167d0cad871SSteve Glendinning
1168d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, BURST_CAP, buf);
1169e3c678e6SSteve Glendinning if (ret < 0) {
1170e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret);
1171e3c678e6SSteve Glendinning return ret;
1172e3c678e6SSteve Glendinning }
1173d0cad871SSteve Glendinning
1174d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, BURST_CAP, &buf);
1175e3c678e6SSteve Glendinning if (ret < 0) {
1176e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret);
1177e3c678e6SSteve Glendinning return ret;
1178e3c678e6SSteve Glendinning }
1179d0cad871SSteve Glendinning
1180d0cad871SSteve Glendinning netif_dbg(dev, ifup, dev->net,
11811e1d7412SJoe Perches "Read Value from BURST_CAP after writing: 0x%08x\n", buf);
1182d0cad871SSteve Glendinning
1183d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY);
1184e3c678e6SSteve Glendinning if (ret < 0) {
1185e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write BULK_IN_DLY: %d\n", ret);
1186e3c678e6SSteve Glendinning return ret;
1187e3c678e6SSteve Glendinning }
1188d0cad871SSteve Glendinning
1189d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, BULK_IN_DLY, &buf);
1190e3c678e6SSteve Glendinning if (ret < 0) {
1191e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret);
1192e3c678e6SSteve Glendinning return ret;
1193e3c678e6SSteve Glendinning }
1194d0cad871SSteve Glendinning
1195d0cad871SSteve Glendinning netif_dbg(dev, ifup, dev->net,
11961e1d7412SJoe Perches "Read Value from BULK_IN_DLY after writing: 0x%08x\n", buf);
1197d0cad871SSteve Glendinning
1198d0cad871SSteve Glendinning if (turbo_mode) {
1199d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
1200e3c678e6SSteve Glendinning if (ret < 0) {
1201e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret);
1202e3c678e6SSteve Glendinning return ret;
1203e3c678e6SSteve Glendinning }
1204d0cad871SSteve Glendinning
12051e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf);
1206d0cad871SSteve Glendinning
1207d0cad871SSteve Glendinning buf |= (HW_CFG_MEF | HW_CFG_BCE);
1208d0cad871SSteve Glendinning
1209d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, HW_CFG, buf);
1210e3c678e6SSteve Glendinning if (ret < 0) {
1211e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret);
1212e3c678e6SSteve Glendinning return ret;
1213e3c678e6SSteve Glendinning }
1214d0cad871SSteve Glendinning
1215d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
1216e3c678e6SSteve Glendinning if (ret < 0) {
1217e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret);
1218e3c678e6SSteve Glendinning return ret;
1219e3c678e6SSteve Glendinning }
1220d0cad871SSteve Glendinning
12211e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf);
1222d0cad871SSteve Glendinning }
1223d0cad871SSteve Glendinning
1224d0cad871SSteve Glendinning /* set FIFO sizes */
1225d0cad871SSteve Glendinning buf = (MAX_RX_FIFO_SIZE - 512) / 512;
1226d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FCT_RX_FIFO_END, buf);
1227e3c678e6SSteve Glendinning if (ret < 0) {
1228e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write FCT_RX_FIFO_END: %d\n", ret);
1229e3c678e6SSteve Glendinning return ret;
1230e3c678e6SSteve Glendinning }
1231d0cad871SSteve Glendinning
12321e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "FCT_RX_FIFO_END set to 0x%08x\n", buf);
1233d0cad871SSteve Glendinning
1234d0cad871SSteve Glendinning buf = (MAX_TX_FIFO_SIZE - 512) / 512;
1235d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FCT_TX_FIFO_END, buf);
1236e3c678e6SSteve Glendinning if (ret < 0) {
1237e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write FCT_TX_FIFO_END: %d\n", ret);
1238e3c678e6SSteve Glendinning return ret;
1239e3c678e6SSteve Glendinning }
1240d0cad871SSteve Glendinning
12411e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "FCT_TX_FIFO_END set to 0x%08x\n", buf);
1242d0cad871SSteve Glendinning
1243d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL);
1244e3c678e6SSteve Glendinning if (ret < 0) {
1245e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write INT_STS: %d\n", ret);
1246e3c678e6SSteve Glendinning return ret;
1247e3c678e6SSteve Glendinning }
1248d0cad871SSteve Glendinning
1249d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, ID_REV, &buf);
1250e3c678e6SSteve Glendinning if (ret < 0) {
1251e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret);
1252e3c678e6SSteve Glendinning return ret;
1253e3c678e6SSteve Glendinning }
1254d0cad871SSteve Glendinning
12551e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", buf);
1256d0cad871SSteve Glendinning
125797138a1cSSteve Glendinning ret = smsc75xx_read_reg(dev, E2P_CMD, &buf);
1258e3c678e6SSteve Glendinning if (ret < 0) {
1259e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read E2P_CMD: %d\n", ret);
1260e3c678e6SSteve Glendinning return ret;
1261e3c678e6SSteve Glendinning }
126297138a1cSSteve Glendinning
126397138a1cSSteve Glendinning /* only set default GPIO/LED settings if no EEPROM is detected */
126497138a1cSSteve Glendinning if (!(buf & E2P_CMD_LOADED)) {
1265d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf);
1266e3c678e6SSteve Glendinning if (ret < 0) {
1267e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read LED_GPIO_CFG: %d\n", ret);
1268e3c678e6SSteve Glendinning return ret;
1269e3c678e6SSteve Glendinning }
1270d0cad871SSteve Glendinning
1271d0cad871SSteve Glendinning buf &= ~(LED_GPIO_CFG_LED2_FUN_SEL | LED_GPIO_CFG_LED10_FUN_SEL);
1272d0cad871SSteve Glendinning buf |= LED_GPIO_CFG_LEDGPIO_EN | LED_GPIO_CFG_LED2_FUN_SEL;
1273d0cad871SSteve Glendinning
1274d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf);
1275e3c678e6SSteve Glendinning if (ret < 0) {
1276e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write LED_GPIO_CFG: %d\n", ret);
1277e3c678e6SSteve Glendinning return ret;
1278e3c678e6SSteve Glendinning }
127997138a1cSSteve Glendinning }
1280d0cad871SSteve Glendinning
1281d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FLOW, 0);
1282e3c678e6SSteve Glendinning if (ret < 0) {
1283e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret);
1284e3c678e6SSteve Glendinning return ret;
1285e3c678e6SSteve Glendinning }
1286d0cad871SSteve Glendinning
1287d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FCT_FLOW, 0);
1288e3c678e6SSteve Glendinning if (ret < 0) {
1289e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write FCT_FLOW: %d\n", ret);
1290e3c678e6SSteve Glendinning return ret;
1291e3c678e6SSteve Glendinning }
1292d0cad871SSteve Glendinning
1293d0cad871SSteve Glendinning /* Don't need rfe_ctl_lock during initialisation */
1294d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl);
1295e3c678e6SSteve Glendinning if (ret < 0) {
1296e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret);
1297e3c678e6SSteve Glendinning return ret;
1298e3c678e6SSteve Glendinning }
1299d0cad871SSteve Glendinning
1300d0cad871SSteve Glendinning pdata->rfe_ctl |= RFE_CTL_AB | RFE_CTL_DPF;
1301d0cad871SSteve Glendinning
1302d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
1303e3c678e6SSteve Glendinning if (ret < 0) {
1304e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write RFE_CTL: %d\n", ret);
1305e3c678e6SSteve Glendinning return ret;
1306e3c678e6SSteve Glendinning }
1307d0cad871SSteve Glendinning
1308d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl);
1309e3c678e6SSteve Glendinning if (ret < 0) {
1310e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret);
1311e3c678e6SSteve Glendinning return ret;
1312e3c678e6SSteve Glendinning }
1313d0cad871SSteve Glendinning
13141e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x\n",
13151e1d7412SJoe Perches pdata->rfe_ctl);
1316d0cad871SSteve Glendinning
1317d0cad871SSteve Glendinning /* Enable or disable checksum offload engines */
131878e47fe4SMichał Mirosław smsc75xx_set_features(dev->net, dev->net->features);
1319d0cad871SSteve Glendinning
1320d0cad871SSteve Glendinning smsc75xx_set_multicast(dev->net);
1321d0cad871SSteve Glendinning
1322d0cad871SSteve Glendinning ret = smsc75xx_phy_initialize(dev);
1323e3c678e6SSteve Glendinning if (ret < 0) {
1324e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to initialize PHY: %d\n", ret);
1325e3c678e6SSteve Glendinning return ret;
1326e3c678e6SSteve Glendinning }
1327d0cad871SSteve Glendinning
1328d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, INT_EP_CTL, &buf);
1329e3c678e6SSteve Glendinning if (ret < 0) {
1330e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret);
1331e3c678e6SSteve Glendinning return ret;
1332e3c678e6SSteve Glendinning }
1333d0cad871SSteve Glendinning
1334d0cad871SSteve Glendinning /* enable PHY interrupts */
1335d0cad871SSteve Glendinning buf |= INT_ENP_PHY_INT;
1336d0cad871SSteve Glendinning
1337d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf);
1338e3c678e6SSteve Glendinning if (ret < 0) {
1339e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret);
1340e3c678e6SSteve Glendinning return ret;
1341e3c678e6SSteve Glendinning }
1342d0cad871SSteve Glendinning
13432f3a081eSSteve Glendinning /* allow mac to detect speed and duplex from phy */
13442f3a081eSSteve Glendinning ret = smsc75xx_read_reg(dev, MAC_CR, &buf);
1345e3c678e6SSteve Glendinning if (ret < 0) {
1346e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret);
1347e3c678e6SSteve Glendinning return ret;
1348e3c678e6SSteve Glendinning }
13492f3a081eSSteve Glendinning
13502f3a081eSSteve Glendinning buf |= (MAC_CR_ADD | MAC_CR_ASD);
13512f3a081eSSteve Glendinning ret = smsc75xx_write_reg(dev, MAC_CR, buf);
1352e3c678e6SSteve Glendinning if (ret < 0) {
1353e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret);
1354e3c678e6SSteve Glendinning return ret;
1355e3c678e6SSteve Glendinning }
13562f3a081eSSteve Glendinning
1357d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, MAC_TX, &buf);
1358e3c678e6SSteve Glendinning if (ret < 0) {
1359e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read MAC_TX: %d\n", ret);
1360e3c678e6SSteve Glendinning return ret;
1361e3c678e6SSteve Glendinning }
1362d0cad871SSteve Glendinning
1363d0cad871SSteve Glendinning buf |= MAC_TX_TXEN;
1364d0cad871SSteve Glendinning
1365d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, MAC_TX, buf);
1366e3c678e6SSteve Glendinning if (ret < 0) {
1367e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_TX: %d\n", ret);
1368e3c678e6SSteve Glendinning return ret;
1369e3c678e6SSteve Glendinning }
1370d0cad871SSteve Glendinning
13711e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf);
1372d0cad871SSteve Glendinning
1373d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf);
1374e3c678e6SSteve Glendinning if (ret < 0) {
1375e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read FCT_TX_CTL: %d\n", ret);
1376e3c678e6SSteve Glendinning return ret;
1377e3c678e6SSteve Glendinning }
1378d0cad871SSteve Glendinning
1379d0cad871SSteve Glendinning buf |= FCT_TX_CTL_EN;
1380d0cad871SSteve Glendinning
1381d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf);
1382e3c678e6SSteve Glendinning if (ret < 0) {
1383e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write FCT_TX_CTL: %d\n", ret);
1384e3c678e6SSteve Glendinning return ret;
1385e3c678e6SSteve Glendinning }
1386d0cad871SSteve Glendinning
13871e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf);
1388d0cad871SSteve Glendinning
13894c51e536SSteve Glendinning ret = smsc75xx_set_rx_max_frame_length(dev, dev->net->mtu + ETH_HLEN);
1390e3c678e6SSteve Glendinning if (ret < 0) {
1391e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to set max rx frame length\n");
1392e3c678e6SSteve Glendinning return ret;
1393e3c678e6SSteve Glendinning }
1394d0cad871SSteve Glendinning
1395d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, MAC_RX, &buf);
1396e3c678e6SSteve Glendinning if (ret < 0) {
1397e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret);
1398e3c678e6SSteve Glendinning return ret;
1399e3c678e6SSteve Glendinning }
1400d0cad871SSteve Glendinning
1401d0cad871SSteve Glendinning buf |= MAC_RX_RXEN;
1402d0cad871SSteve Glendinning
1403d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, MAC_RX, buf);
1404e3c678e6SSteve Glendinning if (ret < 0) {
1405e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret);
1406e3c678e6SSteve Glendinning return ret;
1407e3c678e6SSteve Glendinning }
1408d0cad871SSteve Glendinning
14091e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf);
1410d0cad871SSteve Glendinning
1411d0cad871SSteve Glendinning ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf);
1412e3c678e6SSteve Glendinning if (ret < 0) {
1413e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read FCT_RX_CTL: %d\n", ret);
1414e3c678e6SSteve Glendinning return ret;
1415e3c678e6SSteve Glendinning }
1416d0cad871SSteve Glendinning
1417d0cad871SSteve Glendinning buf |= FCT_RX_CTL_EN;
1418d0cad871SSteve Glendinning
1419d0cad871SSteve Glendinning ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf);
1420e3c678e6SSteve Glendinning if (ret < 0) {
1421e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write FCT_RX_CTL: %d\n", ret);
1422e3c678e6SSteve Glendinning return ret;
1423e3c678e6SSteve Glendinning }
1424d0cad871SSteve Glendinning
14251e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x\n", buf);
1426d0cad871SSteve Glendinning
14271e1d7412SJoe Perches netif_dbg(dev, ifup, dev->net, "smsc75xx_reset, return 0\n");
1428d0cad871SSteve Glendinning return 0;
1429d0cad871SSteve Glendinning }
1430d0cad871SSteve Glendinning
1431d0cad871SSteve Glendinning static const struct net_device_ops smsc75xx_netdev_ops = {
1432d0cad871SSteve Glendinning .ndo_open = usbnet_open,
1433d0cad871SSteve Glendinning .ndo_stop = usbnet_stop,
1434d0cad871SSteve Glendinning .ndo_start_xmit = usbnet_start_xmit,
1435d0cad871SSteve Glendinning .ndo_tx_timeout = usbnet_tx_timeout,
1436323955a0SHeiner Kallweit .ndo_get_stats64 = dev_get_tstats64,
1437d0cad871SSteve Glendinning .ndo_change_mtu = smsc75xx_change_mtu,
1438d0cad871SSteve Glendinning .ndo_set_mac_address = eth_mac_addr,
1439d0cad871SSteve Glendinning .ndo_validate_addr = eth_validate_addr,
1440a7605370SArnd Bergmann .ndo_eth_ioctl = smsc75xx_ioctl,
1441afc4b13dSJiri Pirko .ndo_set_rx_mode = smsc75xx_set_multicast,
144278e47fe4SMichał Mirosław .ndo_set_features = smsc75xx_set_features,
1443d0cad871SSteve Glendinning };
1444d0cad871SSteve Glendinning
smsc75xx_bind(struct usbnet * dev,struct usb_interface * intf)1445d0cad871SSteve Glendinning static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)
1446d0cad871SSteve Glendinning {
1447d0cad871SSteve Glendinning struct smsc75xx_priv *pdata = NULL;
1448d0cad871SSteve Glendinning int ret;
1449d0cad871SSteve Glendinning
1450d0cad871SSteve Glendinning printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n");
1451d0cad871SSteve Glendinning
1452d0cad871SSteve Glendinning ret = usbnet_get_endpoints(dev, intf);
1453e3c678e6SSteve Glendinning if (ret < 0) {
1454e3c678e6SSteve Glendinning netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret);
1455e3c678e6SSteve Glendinning return ret;
1456e3c678e6SSteve Glendinning }
1457d0cad871SSteve Glendinning
1458d0cad871SSteve Glendinning dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc75xx_priv),
1459d0cad871SSteve Glendinning GFP_KERNEL);
1460d0cad871SSteve Glendinning
1461d0cad871SSteve Glendinning pdata = (struct smsc75xx_priv *)(dev->data[0]);
146238673c82SJoe Perches if (!pdata)
1463d0cad871SSteve Glendinning return -ENOMEM;
1464d0cad871SSteve Glendinning
1465d0cad871SSteve Glendinning pdata->dev = dev;
1466d0cad871SSteve Glendinning
1467d0cad871SSteve Glendinning spin_lock_init(&pdata->rfe_ctl_lock);
1468d0cad871SSteve Glendinning mutex_init(&pdata->dataport_mutex);
1469d0cad871SSteve Glendinning
1470d0cad871SSteve Glendinning INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write);
1471d0cad871SSteve Glendinning
147220f01703SEric Dumazet if (DEFAULT_TX_CSUM_ENABLE)
147378e47fe4SMichał Mirosław dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
147420f01703SEric Dumazet
147578e47fe4SMichał Mirosław if (DEFAULT_RX_CSUM_ENABLE)
147678e47fe4SMichał Mirosław dev->net->features |= NETIF_F_RXCSUM;
1477d0cad871SSteve Glendinning
147878e47fe4SMichał Mirosław dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
147920f01703SEric Dumazet NETIF_F_RXCSUM;
1480d0cad871SSteve Glendinning
1481481705a1SSteve Glendinning ret = smsc75xx_wait_ready(dev, 0);
1482481705a1SSteve Glendinning if (ret < 0) {
1483481705a1SSteve Glendinning netdev_warn(dev->net, "device not ready in smsc75xx_bind\n");
148456b786d8SDongliang Mu goto free_pdata;
1485481705a1SSteve Glendinning }
1486481705a1SSteve Glendinning
1487481705a1SSteve Glendinning smsc75xx_init_mac_address(dev);
1488481705a1SSteve Glendinning
1489d0cad871SSteve Glendinning /* Init all registers */
1490d0cad871SSteve Glendinning ret = smsc75xx_reset(dev);
1491e3c678e6SSteve Glendinning if (ret < 0) {
1492e3c678e6SSteve Glendinning netdev_warn(dev->net, "smsc75xx_reset error %d\n", ret);
149356b786d8SDongliang Mu goto cancel_work;
1494e3c678e6SSteve Glendinning }
1495d0cad871SSteve Glendinning
1496d0cad871SSteve Glendinning dev->net->netdev_ops = &smsc75xx_netdev_ops;
1497d0cad871SSteve Glendinning dev->net->ethtool_ops = &smsc75xx_ethtool_ops;
1498d0cad871SSteve Glendinning dev->net->flags |= IFF_MULTICAST;
1499d0cad871SSteve Glendinning dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD;
1500a99ff7d0SStephane Fillod dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
1501f77f0aeeSJarod Wilson dev->net->max_mtu = MAX_SINGLE_PACKET_SIZE;
1502d0cad871SSteve Glendinning return 0;
150346a8b29cSPavel Skripkin
150456b786d8SDongliang Mu cancel_work:
150556b786d8SDongliang Mu cancel_work_sync(&pdata->set_multicast);
150656b786d8SDongliang Mu free_pdata:
150746a8b29cSPavel Skripkin kfree(pdata);
150856b786d8SDongliang Mu dev->data[0] = 0;
150946a8b29cSPavel Skripkin return ret;
1510d0cad871SSteve Glendinning }
1511d0cad871SSteve Glendinning
smsc75xx_unbind(struct usbnet * dev,struct usb_interface * intf)1512d0cad871SSteve Glendinning static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf)
1513d0cad871SSteve Glendinning {
1514d0cad871SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1515d0cad871SSteve Glendinning if (pdata) {
1516f7b2a56eSYu Zhao cancel_work_sync(&pdata->set_multicast);
15171e1d7412SJoe Perches netif_dbg(dev, ifdown, dev->net, "free pdata\n");
1518d0cad871SSteve Glendinning kfree(pdata);
1519d0cad871SSteve Glendinning dev->data[0] = 0;
1520d0cad871SSteve Glendinning }
1521d0cad871SSteve Glendinning }
1522d0cad871SSteve Glendinning
smsc_crc(const u8 * buffer,size_t len)1523899a391bSSteve Glendinning static u16 smsc_crc(const u8 *buffer, size_t len)
1524899a391bSSteve Glendinning {
1525899a391bSSteve Glendinning return bitrev16(crc16(0xFFFF, buffer, len));
1526899a391bSSteve Glendinning }
1527899a391bSSteve Glendinning
smsc75xx_write_wuff(struct usbnet * dev,int filter,u32 wuf_cfg,u32 wuf_mask1)1528899a391bSSteve Glendinning static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg,
1529899a391bSSteve Glendinning u32 wuf_mask1)
1530899a391bSSteve Glendinning {
1531899a391bSSteve Glendinning int cfg_base = WUF_CFGX + filter * 4;
1532899a391bSSteve Glendinning int mask_base = WUF_MASKX + filter * 16;
1533899a391bSSteve Glendinning int ret;
1534899a391bSSteve Glendinning
1535899a391bSSteve Glendinning ret = smsc75xx_write_reg(dev, cfg_base, wuf_cfg);
1536e3c678e6SSteve Glendinning if (ret < 0) {
1537e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUF_CFGX\n");
1538e3c678e6SSteve Glendinning return ret;
1539e3c678e6SSteve Glendinning }
1540899a391bSSteve Glendinning
1541899a391bSSteve Glendinning ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1);
1542e3c678e6SSteve Glendinning if (ret < 0) {
1543e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUF_MASKX\n");
1544e3c678e6SSteve Glendinning return ret;
1545e3c678e6SSteve Glendinning }
1546899a391bSSteve Glendinning
1547899a391bSSteve Glendinning ret = smsc75xx_write_reg(dev, mask_base + 4, 0);
1548e3c678e6SSteve Glendinning if (ret < 0) {
1549e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUF_MASKX\n");
1550e3c678e6SSteve Glendinning return ret;
1551e3c678e6SSteve Glendinning }
1552899a391bSSteve Glendinning
1553899a391bSSteve Glendinning ret = smsc75xx_write_reg(dev, mask_base + 8, 0);
1554e3c678e6SSteve Glendinning if (ret < 0) {
1555e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUF_MASKX\n");
1556e3c678e6SSteve Glendinning return ret;
1557e3c678e6SSteve Glendinning }
1558899a391bSSteve Glendinning
1559899a391bSSteve Glendinning ret = smsc75xx_write_reg(dev, mask_base + 12, 0);
1560e3c678e6SSteve Glendinning if (ret < 0) {
1561e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUF_MASKX\n");
1562e3c678e6SSteve Glendinning return ret;
1563e3c678e6SSteve Glendinning }
1564899a391bSSteve Glendinning
1565899a391bSSteve Glendinning return 0;
1566899a391bSSteve Glendinning }
1567899a391bSSteve Glendinning
smsc75xx_enter_suspend0(struct usbnet * dev)15689deb2757SSteve Glendinning static int smsc75xx_enter_suspend0(struct usbnet *dev)
15699deb2757SSteve Glendinning {
1570b4cdea9cSSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
15719deb2757SSteve Glendinning u32 val;
15729deb2757SSteve Glendinning int ret;
15739deb2757SSteve Glendinning
15749deb2757SSteve Glendinning ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1575e3c678e6SSteve Glendinning if (ret < 0) {
1576e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
1577e3c678e6SSteve Glendinning return ret;
1578e3c678e6SSteve Glendinning }
15799deb2757SSteve Glendinning
15809deb2757SSteve Glendinning val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST));
15819deb2757SSteve Glendinning val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS;
15829deb2757SSteve Glendinning
15839deb2757SSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1584e3c678e6SSteve Glendinning if (ret < 0) {
1585e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1586e3c678e6SSteve Glendinning return ret;
1587e3c678e6SSteve Glendinning }
15889deb2757SSteve Glendinning
1589351f33d9SSteve Glendinning pdata->suspend_flags |= SUSPEND_SUSPEND0;
1590b4cdea9cSSteve Glendinning
15919deb2757SSteve Glendinning return 0;
15929deb2757SSteve Glendinning }
15939deb2757SSteve Glendinning
smsc75xx_enter_suspend1(struct usbnet * dev)1594f329ccdcSSteve Glendinning static int smsc75xx_enter_suspend1(struct usbnet *dev)
1595f329ccdcSSteve Glendinning {
1596b4cdea9cSSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1597f329ccdcSSteve Glendinning u32 val;
1598f329ccdcSSteve Glendinning int ret;
1599f329ccdcSSteve Glendinning
1600f329ccdcSSteve Glendinning ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1601e3c678e6SSteve Glendinning if (ret < 0) {
1602e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
1603e3c678e6SSteve Glendinning return ret;
1604e3c678e6SSteve Glendinning }
1605f329ccdcSSteve Glendinning
1606f329ccdcSSteve Glendinning val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
1607f329ccdcSSteve Glendinning val |= PMT_CTL_SUS_MODE_1;
1608f329ccdcSSteve Glendinning
1609f329ccdcSSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1610e3c678e6SSteve Glendinning if (ret < 0) {
1611e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1612e3c678e6SSteve Glendinning return ret;
1613e3c678e6SSteve Glendinning }
1614f329ccdcSSteve Glendinning
1615f329ccdcSSteve Glendinning /* clear wol status, enable energy detection */
1616f329ccdcSSteve Glendinning val &= ~PMT_CTL_WUPS;
1617f329ccdcSSteve Glendinning val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN);
1618f329ccdcSSteve Glendinning
1619f329ccdcSSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1620e3c678e6SSteve Glendinning if (ret < 0) {
1621e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1622e3c678e6SSteve Glendinning return ret;
1623e3c678e6SSteve Glendinning }
1624f329ccdcSSteve Glendinning
1625351f33d9SSteve Glendinning pdata->suspend_flags |= SUSPEND_SUSPEND1;
1626b4cdea9cSSteve Glendinning
1627f329ccdcSSteve Glendinning return 0;
1628f329ccdcSSteve Glendinning }
1629f329ccdcSSteve Glendinning
smsc75xx_enter_suspend2(struct usbnet * dev)16309deb2757SSteve Glendinning static int smsc75xx_enter_suspend2(struct usbnet *dev)
16319deb2757SSteve Glendinning {
1632b4cdea9cSSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
16339deb2757SSteve Glendinning u32 val;
16349deb2757SSteve Glendinning int ret;
16359deb2757SSteve Glendinning
16369deb2757SSteve Glendinning ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1637e3c678e6SSteve Glendinning if (ret < 0) {
1638e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
1639e3c678e6SSteve Glendinning return ret;
1640e3c678e6SSteve Glendinning }
16419deb2757SSteve Glendinning
16429deb2757SSteve Glendinning val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
16439deb2757SSteve Glendinning val |= PMT_CTL_SUS_MODE_2;
16449deb2757SSteve Glendinning
16459deb2757SSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1646e3c678e6SSteve Glendinning if (ret < 0) {
1647e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1648e3c678e6SSteve Glendinning return ret;
1649e3c678e6SSteve Glendinning }
16509deb2757SSteve Glendinning
1651b4cdea9cSSteve Glendinning pdata->suspend_flags |= SUSPEND_SUSPEND2;
1652b4cdea9cSSteve Glendinning
1653b4cdea9cSSteve Glendinning return 0;
1654b4cdea9cSSteve Glendinning }
1655b4cdea9cSSteve Glendinning
smsc75xx_enter_suspend3(struct usbnet * dev)1656b4cdea9cSSteve Glendinning static int smsc75xx_enter_suspend3(struct usbnet *dev)
1657b4cdea9cSSteve Glendinning {
1658b4cdea9cSSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1659b4cdea9cSSteve Glendinning u32 val;
1660b4cdea9cSSteve Glendinning int ret;
1661b4cdea9cSSteve Glendinning
1662b4cdea9cSSteve Glendinning ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val);
1663e3c678e6SSteve Glendinning if (ret < 0) {
1664e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading FCT_RX_CTL\n");
1665e3c678e6SSteve Glendinning return ret;
1666e3c678e6SSteve Glendinning }
1667b4cdea9cSSteve Glendinning
1668b4cdea9cSSteve Glendinning if (val & FCT_RX_CTL_RXUSED) {
1669b4cdea9cSSteve Glendinning netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n");
1670b4cdea9cSSteve Glendinning return -EBUSY;
1671b4cdea9cSSteve Glendinning }
1672b4cdea9cSSteve Glendinning
1673b4cdea9cSSteve Glendinning ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1674e3c678e6SSteve Glendinning if (ret < 0) {
1675e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
1676e3c678e6SSteve Glendinning return ret;
1677e3c678e6SSteve Glendinning }
1678b4cdea9cSSteve Glendinning
1679b4cdea9cSSteve Glendinning val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
1680b4cdea9cSSteve Glendinning val |= PMT_CTL_SUS_MODE_3 | PMT_CTL_RES_CLR_WKP_EN;
1681b4cdea9cSSteve Glendinning
1682b4cdea9cSSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1683e3c678e6SSteve Glendinning if (ret < 0) {
1684e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1685e3c678e6SSteve Glendinning return ret;
1686e3c678e6SSteve Glendinning }
1687b4cdea9cSSteve Glendinning
1688b4cdea9cSSteve Glendinning /* clear wol status */
1689b4cdea9cSSteve Glendinning val &= ~PMT_CTL_WUPS;
1690b4cdea9cSSteve Glendinning val |= PMT_CTL_WUPS_WOL;
1691b4cdea9cSSteve Glendinning
1692b4cdea9cSSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1693e3c678e6SSteve Glendinning if (ret < 0) {
1694e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1695e3c678e6SSteve Glendinning return ret;
1696e3c678e6SSteve Glendinning }
1697b4cdea9cSSteve Glendinning
1698351f33d9SSteve Glendinning pdata->suspend_flags |= SUSPEND_SUSPEND3;
1699b4cdea9cSSteve Glendinning
17009deb2757SSteve Glendinning return 0;
17019deb2757SSteve Glendinning }
17029deb2757SSteve Glendinning
smsc75xx_enable_phy_wakeup_interrupts(struct usbnet * dev,u16 mask)1703f329ccdcSSteve Glendinning static int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask)
1704f329ccdcSSteve Glendinning {
1705f329ccdcSSteve Glendinning struct mii_if_info *mii = &dev->mii;
1706f329ccdcSSteve Glendinning int ret;
1707f329ccdcSSteve Glendinning
1708f329ccdcSSteve Glendinning netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n");
1709f329ccdcSSteve Glendinning
1710f329ccdcSSteve Glendinning /* read to clear */
1711f329ccdcSSteve Glendinning ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC);
1712e3c678e6SSteve Glendinning if (ret < 0) {
1713e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PHY_INT_SRC\n");
1714e3c678e6SSteve Glendinning return ret;
1715e3c678e6SSteve Glendinning }
1716f329ccdcSSteve Glendinning
1717f329ccdcSSteve Glendinning /* enable interrupt source */
1718f329ccdcSSteve Glendinning ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK);
1719e3c678e6SSteve Glendinning if (ret < 0) {
1720e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PHY_INT_MASK\n");
1721e3c678e6SSteve Glendinning return ret;
1722e3c678e6SSteve Glendinning }
1723f329ccdcSSteve Glendinning
1724f329ccdcSSteve Glendinning ret |= mask;
1725f329ccdcSSteve Glendinning
1726f329ccdcSSteve Glendinning smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret);
1727f329ccdcSSteve Glendinning
1728f329ccdcSSteve Glendinning return 0;
1729f329ccdcSSteve Glendinning }
1730f329ccdcSSteve Glendinning
smsc75xx_link_ok_nopm(struct usbnet * dev)1731f329ccdcSSteve Glendinning static int smsc75xx_link_ok_nopm(struct usbnet *dev)
1732f329ccdcSSteve Glendinning {
1733f329ccdcSSteve Glendinning struct mii_if_info *mii = &dev->mii;
1734f329ccdcSSteve Glendinning int ret;
1735f329ccdcSSteve Glendinning
1736f329ccdcSSteve Glendinning /* first, a dummy read, needed to latch some MII phys */
1737f329ccdcSSteve Glendinning ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR);
1738e3c678e6SSteve Glendinning if (ret < 0) {
1739e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading MII_BMSR\n");
1740e3c678e6SSteve Glendinning return ret;
1741e3c678e6SSteve Glendinning }
1742f329ccdcSSteve Glendinning
1743f329ccdcSSteve Glendinning ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR);
1744e3c678e6SSteve Glendinning if (ret < 0) {
1745e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading MII_BMSR\n");
1746e3c678e6SSteve Glendinning return ret;
1747e3c678e6SSteve Glendinning }
1748f329ccdcSSteve Glendinning
1749f329ccdcSSteve Glendinning return !!(ret & BMSR_LSTATUS);
1750f329ccdcSSteve Glendinning }
1751f329ccdcSSteve Glendinning
smsc75xx_autosuspend(struct usbnet * dev,u32 link_up)1752b4cdea9cSSteve Glendinning static int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up)
1753b4cdea9cSSteve Glendinning {
1754b4cdea9cSSteve Glendinning int ret;
1755b4cdea9cSSteve Glendinning
1756b4cdea9cSSteve Glendinning if (!netif_running(dev->net)) {
1757b4cdea9cSSteve Glendinning /* interface is ifconfig down so fully power down hw */
1758b4cdea9cSSteve Glendinning netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n");
1759b4cdea9cSSteve Glendinning return smsc75xx_enter_suspend2(dev);
1760b4cdea9cSSteve Glendinning }
1761b4cdea9cSSteve Glendinning
1762b4cdea9cSSteve Glendinning if (!link_up) {
1763b4cdea9cSSteve Glendinning /* link is down so enter EDPD mode */
1764b4cdea9cSSteve Glendinning netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
1765b4cdea9cSSteve Glendinning
1766b4cdea9cSSteve Glendinning /* enable PHY wakeup events for if cable is attached */
1767b4cdea9cSSteve Glendinning ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
1768b4cdea9cSSteve Glendinning PHY_INT_MASK_ANEG_COMP);
1769e3c678e6SSteve Glendinning if (ret < 0) {
1770e3c678e6SSteve Glendinning netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
1771e3c678e6SSteve Glendinning return ret;
1772e3c678e6SSteve Glendinning }
1773b4cdea9cSSteve Glendinning
1774b4cdea9cSSteve Glendinning netdev_info(dev->net, "entering SUSPEND1 mode\n");
1775b4cdea9cSSteve Glendinning return smsc75xx_enter_suspend1(dev);
1776b4cdea9cSSteve Glendinning }
1777b4cdea9cSSteve Glendinning
1778b4cdea9cSSteve Glendinning /* enable PHY wakeup events so we remote wakeup if cable is pulled */
1779b4cdea9cSSteve Glendinning ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
1780b4cdea9cSSteve Glendinning PHY_INT_MASK_LINK_DOWN);
1781e3c678e6SSteve Glendinning if (ret < 0) {
1782e3c678e6SSteve Glendinning netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
1783e3c678e6SSteve Glendinning return ret;
1784e3c678e6SSteve Glendinning }
1785b4cdea9cSSteve Glendinning
1786b4cdea9cSSteve Glendinning netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n");
1787b4cdea9cSSteve Glendinning return smsc75xx_enter_suspend3(dev);
1788b4cdea9cSSteve Glendinning }
1789b4cdea9cSSteve Glendinning
smsc75xx_suspend(struct usb_interface * intf,pm_message_t message)179016c79a04SSteve Glendinning static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
179116c79a04SSteve Glendinning {
179216c79a04SSteve Glendinning struct usbnet *dev = usb_get_intfdata(intf);
17936c636503SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1794f329ccdcSSteve Glendinning u32 val, link_up;
179516c79a04SSteve Glendinning int ret;
179616c79a04SSteve Glendinning
179716c79a04SSteve Glendinning ret = usbnet_suspend(intf, message);
1798e3c678e6SSteve Glendinning if (ret < 0) {
1799e3c678e6SSteve Glendinning netdev_warn(dev->net, "usbnet_suspend error\n");
1800e3c678e6SSteve Glendinning return ret;
1801e3c678e6SSteve Glendinning }
180216c79a04SSteve Glendinning
1803b4cdea9cSSteve Glendinning if (pdata->suspend_flags) {
1804b4cdea9cSSteve Glendinning netdev_warn(dev->net, "error during last resume\n");
1805b4cdea9cSSteve Glendinning pdata->suspend_flags = 0;
1806b4cdea9cSSteve Glendinning }
1807b4cdea9cSSteve Glendinning
1808f329ccdcSSteve Glendinning /* determine if link is up using only _nopm functions */
1809f329ccdcSSteve Glendinning link_up = smsc75xx_link_ok_nopm(dev);
1810f329ccdcSSteve Glendinning
1811b4cdea9cSSteve Glendinning if (message.event == PM_EVENT_AUTO_SUSPEND) {
1812b4cdea9cSSteve Glendinning ret = smsc75xx_autosuspend(dev, link_up);
1813b4cdea9cSSteve Glendinning goto done;
1814b4cdea9cSSteve Glendinning }
1815b4cdea9cSSteve Glendinning
1816b4cdea9cSSteve Glendinning /* if we get this far we're not autosuspending */
1817f329ccdcSSteve Glendinning /* if no wol options set, or if link is down and we're not waking on
1818f329ccdcSSteve Glendinning * PHY activity, enter lowest power SUSPEND2 mode
1819f329ccdcSSteve Glendinning */
1820f329ccdcSSteve Glendinning if (!(pdata->wolopts & SUPPORTED_WAKE) ||
1821f329ccdcSSteve Glendinning !(link_up || (pdata->wolopts & WAKE_PHY))) {
18221e1d7412SJoe Perches netdev_info(dev->net, "entering SUSPEND2 mode\n");
182316c79a04SSteve Glendinning
18246c636503SSteve Glendinning /* disable energy detect (link up) & wake up events */
182547bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
1826e3c678e6SSteve Glendinning if (ret < 0) {
1827e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
1828e3c678e6SSteve Glendinning goto done;
1829e3c678e6SSteve Glendinning }
18306c636503SSteve Glendinning
18316c636503SSteve Glendinning val &= ~(WUCSR_MPEN | WUCSR_WUEN);
18326c636503SSteve Glendinning
183347bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
1834e3c678e6SSteve Glendinning if (ret < 0) {
1835e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
1836e3c678e6SSteve Glendinning goto done;
1837e3c678e6SSteve Glendinning }
18386c636503SSteve Glendinning
183947bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1840e3c678e6SSteve Glendinning if (ret < 0) {
1841e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
1842e3c678e6SSteve Glendinning goto done;
1843e3c678e6SSteve Glendinning }
18446c636503SSteve Glendinning
18456c636503SSteve Glendinning val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN);
18466c636503SSteve Glendinning
184747bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1848e3c678e6SSteve Glendinning if (ret < 0) {
1849e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
1850e3c678e6SSteve Glendinning goto done;
1851e3c678e6SSteve Glendinning }
18526c636503SSteve Glendinning
1853eacdd6c2SSteve Glendinning ret = smsc75xx_enter_suspend2(dev);
1854eacdd6c2SSteve Glendinning goto done;
185516c79a04SSteve Glendinning }
185616c79a04SSteve Glendinning
1857f329ccdcSSteve Glendinning if (pdata->wolopts & WAKE_PHY) {
1858f329ccdcSSteve Glendinning ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
1859f329ccdcSSteve Glendinning (PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN));
1860e3c678e6SSteve Glendinning if (ret < 0) {
1861e3c678e6SSteve Glendinning netdev_warn(dev->net, "error enabling PHY wakeup ints\n");
1862e3c678e6SSteve Glendinning goto done;
1863e3c678e6SSteve Glendinning }
1864f329ccdcSSteve Glendinning
1865f329ccdcSSteve Glendinning /* if link is down then configure EDPD and enter SUSPEND1,
1866f329ccdcSSteve Glendinning * otherwise enter SUSPEND0 below
1867f329ccdcSSteve Glendinning */
1868f329ccdcSSteve Glendinning if (!link_up) {
1869f329ccdcSSteve Glendinning struct mii_if_info *mii = &dev->mii;
1870f329ccdcSSteve Glendinning netdev_info(dev->net, "entering SUSPEND1 mode\n");
1871f329ccdcSSteve Glendinning
1872f329ccdcSSteve Glendinning /* enable energy detect power-down mode */
1873f329ccdcSSteve Glendinning ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id,
1874f329ccdcSSteve Glendinning PHY_MODE_CTRL_STS);
1875e3c678e6SSteve Glendinning if (ret < 0) {
1876e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n");
1877e3c678e6SSteve Glendinning goto done;
1878e3c678e6SSteve Glendinning }
1879f329ccdcSSteve Glendinning
1880f329ccdcSSteve Glendinning ret |= MODE_CTRL_STS_EDPWRDOWN;
1881f329ccdcSSteve Glendinning
1882f329ccdcSSteve Glendinning smsc75xx_mdio_write_nopm(dev->net, mii->phy_id,
1883f329ccdcSSteve Glendinning PHY_MODE_CTRL_STS, ret);
1884f329ccdcSSteve Glendinning
1885f329ccdcSSteve Glendinning /* enter SUSPEND1 mode */
1886eacdd6c2SSteve Glendinning ret = smsc75xx_enter_suspend1(dev);
1887eacdd6c2SSteve Glendinning goto done;
1888f329ccdcSSteve Glendinning }
1889f329ccdcSSteve Glendinning }
1890f329ccdcSSteve Glendinning
1891899a391bSSteve Glendinning if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) {
1892899a391bSSteve Glendinning int i, filter = 0;
1893899a391bSSteve Glendinning
1894899a391bSSteve Glendinning /* disable all filters */
1895899a391bSSteve Glendinning for (i = 0; i < WUF_NUM; i++) {
189647bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUF_CFGX + i * 4, 0);
1897e3c678e6SSteve Glendinning if (ret < 0) {
1898e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUF_CFGX\n");
1899e3c678e6SSteve Glendinning goto done;
1900e3c678e6SSteve Glendinning }
1901899a391bSSteve Glendinning }
1902899a391bSSteve Glendinning
1903899a391bSSteve Glendinning if (pdata->wolopts & WAKE_MCAST) {
1904899a391bSSteve Glendinning const u8 mcast[] = {0x01, 0x00, 0x5E};
19051e1d7412SJoe Perches netdev_info(dev->net, "enabling multicast detection\n");
1906899a391bSSteve Glendinning
1907899a391bSSteve Glendinning val = WUF_CFGX_EN | WUF_CFGX_ATYPE_MULTICAST
1908899a391bSSteve Glendinning | smsc_crc(mcast, 3);
1909899a391bSSteve Glendinning ret = smsc75xx_write_wuff(dev, filter++, val, 0x0007);
1910e3c678e6SSteve Glendinning if (ret < 0) {
1911e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing wakeup filter\n");
1912e3c678e6SSteve Glendinning goto done;
1913e3c678e6SSteve Glendinning }
1914899a391bSSteve Glendinning }
1915899a391bSSteve Glendinning
1916899a391bSSteve Glendinning if (pdata->wolopts & WAKE_ARP) {
1917899a391bSSteve Glendinning const u8 arp[] = {0x08, 0x06};
19181e1d7412SJoe Perches netdev_info(dev->net, "enabling ARP detection\n");
1919899a391bSSteve Glendinning
1920899a391bSSteve Glendinning val = WUF_CFGX_EN | WUF_CFGX_ATYPE_ALL | (0x0C << 16)
1921899a391bSSteve Glendinning | smsc_crc(arp, 2);
1922899a391bSSteve Glendinning ret = smsc75xx_write_wuff(dev, filter++, val, 0x0003);
1923e3c678e6SSteve Glendinning if (ret < 0) {
1924e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing wakeup filter\n");
1925e3c678e6SSteve Glendinning goto done;
1926e3c678e6SSteve Glendinning }
1927899a391bSSteve Glendinning }
1928899a391bSSteve Glendinning
1929899a391bSSteve Glendinning /* clear any pending pattern match packet status */
193047bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
1931e3c678e6SSteve Glendinning if (ret < 0) {
1932e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
1933e3c678e6SSteve Glendinning goto done;
1934e3c678e6SSteve Glendinning }
19356c636503SSteve Glendinning
1936899a391bSSteve Glendinning val |= WUCSR_WUFR;
1937899a391bSSteve Glendinning
193847bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
1939e3c678e6SSteve Glendinning if (ret < 0) {
1940e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
1941e3c678e6SSteve Glendinning goto done;
1942e3c678e6SSteve Glendinning }
1943899a391bSSteve Glendinning
19441e1d7412SJoe Perches netdev_info(dev->net, "enabling packet match detection\n");
194547bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
1946e3c678e6SSteve Glendinning if (ret < 0) {
1947e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
1948e3c678e6SSteve Glendinning goto done;
1949e3c678e6SSteve Glendinning }
1950899a391bSSteve Glendinning
1951899a391bSSteve Glendinning val |= WUCSR_WUEN;
1952899a391bSSteve Glendinning
195347bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
1954e3c678e6SSteve Glendinning if (ret < 0) {
1955e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
1956e3c678e6SSteve Glendinning goto done;
1957e3c678e6SSteve Glendinning }
1958899a391bSSteve Glendinning } else {
19591e1d7412SJoe Perches netdev_info(dev->net, "disabling packet match detection\n");
196047bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
1961e3c678e6SSteve Glendinning if (ret < 0) {
1962e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
1963e3c678e6SSteve Glendinning goto done;
1964e3c678e6SSteve Glendinning }
1965899a391bSSteve Glendinning
1966899a391bSSteve Glendinning val &= ~WUCSR_WUEN;
19676c636503SSteve Glendinning
196847bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
1969e3c678e6SSteve Glendinning if (ret < 0) {
1970e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
1971e3c678e6SSteve Glendinning goto done;
1972e3c678e6SSteve Glendinning }
19736c636503SSteve Glendinning }
19746c636503SSteve Glendinning
1975899a391bSSteve Glendinning /* disable magic, bcast & unicast wakeup sources */
197647bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
1977e3c678e6SSteve Glendinning if (ret < 0) {
1978e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
1979e3c678e6SSteve Glendinning goto done;
1980e3c678e6SSteve Glendinning }
19816c636503SSteve Glendinning
1982899a391bSSteve Glendinning val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN);
1983899a391bSSteve Glendinning
198447bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
1985e3c678e6SSteve Glendinning if (ret < 0) {
1986e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
1987e3c678e6SSteve Glendinning goto done;
1988e3c678e6SSteve Glendinning }
1989899a391bSSteve Glendinning
1990f329ccdcSSteve Glendinning if (pdata->wolopts & WAKE_PHY) {
1991f329ccdcSSteve Glendinning netdev_info(dev->net, "enabling PHY wakeup\n");
1992f329ccdcSSteve Glendinning
1993f329ccdcSSteve Glendinning ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1994e3c678e6SSteve Glendinning if (ret < 0) {
1995e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
1996e3c678e6SSteve Glendinning goto done;
1997e3c678e6SSteve Glendinning }
1998f329ccdcSSteve Glendinning
1999f329ccdcSSteve Glendinning /* clear wol status, enable energy detection */
2000f329ccdcSSteve Glendinning val &= ~PMT_CTL_WUPS;
2001f329ccdcSSteve Glendinning val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN);
2002f329ccdcSSteve Glendinning
2003f329ccdcSSteve Glendinning ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
2004e3c678e6SSteve Glendinning if (ret < 0) {
2005e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
2006e3c678e6SSteve Glendinning goto done;
2007e3c678e6SSteve Glendinning }
2008f329ccdcSSteve Glendinning }
2009f329ccdcSSteve Glendinning
20106c636503SSteve Glendinning if (pdata->wolopts & WAKE_MAGIC) {
20111e1d7412SJoe Perches netdev_info(dev->net, "enabling magic packet wakeup\n");
201247bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
2013e3c678e6SSteve Glendinning if (ret < 0) {
2014e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
2015e3c678e6SSteve Glendinning goto done;
2016e3c678e6SSteve Glendinning }
2017899a391bSSteve Glendinning
2018899a391bSSteve Glendinning /* clear any pending magic packet status */
2019899a391bSSteve Glendinning val |= WUCSR_MPR | WUCSR_MPEN;
20206c636503SSteve Glendinning
202147bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
2022e3c678e6SSteve Glendinning if (ret < 0) {
2023e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
2024e3c678e6SSteve Glendinning goto done;
2025e3c678e6SSteve Glendinning }
2026899a391bSSteve Glendinning }
20276c636503SSteve Glendinning
2028899a391bSSteve Glendinning if (pdata->wolopts & WAKE_BCAST) {
20291e1d7412SJoe Perches netdev_info(dev->net, "enabling broadcast detection\n");
203047bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
2031e3c678e6SSteve Glendinning if (ret < 0) {
2032e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
2033e3c678e6SSteve Glendinning goto done;
2034e3c678e6SSteve Glendinning }
20356c636503SSteve Glendinning
2036899a391bSSteve Glendinning val |= WUCSR_BCAST_FR | WUCSR_BCST_EN;
20376c636503SSteve Glendinning
203847bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
2039e3c678e6SSteve Glendinning if (ret < 0) {
2040e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
2041e3c678e6SSteve Glendinning goto done;
2042e3c678e6SSteve Glendinning }
2043899a391bSSteve Glendinning }
20446c636503SSteve Glendinning
2045899a391bSSteve Glendinning if (pdata->wolopts & WAKE_UCAST) {
20461e1d7412SJoe Perches netdev_info(dev->net, "enabling unicast detection\n");
204747bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
2048e3c678e6SSteve Glendinning if (ret < 0) {
2049e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
2050e3c678e6SSteve Glendinning goto done;
2051e3c678e6SSteve Glendinning }
2052899a391bSSteve Glendinning
2053899a391bSSteve Glendinning val |= WUCSR_WUFR | WUCSR_PFDA_EN;
2054899a391bSSteve Glendinning
205547bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
2056e3c678e6SSteve Glendinning if (ret < 0) {
2057e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
2058e3c678e6SSteve Glendinning goto done;
2059e3c678e6SSteve Glendinning }
2060899a391bSSteve Glendinning }
2061899a391bSSteve Glendinning
2062899a391bSSteve Glendinning /* enable receiver to enable frame reception */
206347bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val);
2064e3c678e6SSteve Glendinning if (ret < 0) {
2065e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret);
2066e3c678e6SSteve Glendinning goto done;
2067e3c678e6SSteve Glendinning }
20686c636503SSteve Glendinning
20696c636503SSteve Glendinning val |= MAC_RX_RXEN;
20706c636503SSteve Glendinning
207147bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val);
2072e3c678e6SSteve Glendinning if (ret < 0) {
2073e3c678e6SSteve Glendinning netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret);
2074e3c678e6SSteve Glendinning goto done;
2075e3c678e6SSteve Glendinning }
20766c636503SSteve Glendinning
20776c636503SSteve Glendinning /* some wol options are enabled, so enter SUSPEND0 */
20781e1d7412SJoe Perches netdev_info(dev->net, "entering SUSPEND0 mode\n");
2079eacdd6c2SSteve Glendinning ret = smsc75xx_enter_suspend0(dev);
2080eacdd6c2SSteve Glendinning
2081eacdd6c2SSteve Glendinning done:
20825410a473SMing Lei /*
20835410a473SMing Lei * TODO: resume() might need to handle the suspend failure
20845410a473SMing Lei * in system sleep
20855410a473SMing Lei */
20865410a473SMing Lei if (ret && PMSG_IS_AUTO(message))
2087eacdd6c2SSteve Glendinning usbnet_resume(intf);
2088eacdd6c2SSteve Glendinning return ret;
20896c636503SSteve Glendinning }
20906c636503SSteve Glendinning
smsc75xx_resume(struct usb_interface * intf)209116c79a04SSteve Glendinning static int smsc75xx_resume(struct usb_interface *intf)
209216c79a04SSteve Glendinning {
209316c79a04SSteve Glendinning struct usbnet *dev = usb_get_intfdata(intf);
20946c636503SSteve Glendinning struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
2095b4cdea9cSSteve Glendinning u8 suspend_flags = pdata->suspend_flags;
209616c79a04SSteve Glendinning int ret;
209716c79a04SSteve Glendinning u32 val;
209816c79a04SSteve Glendinning
2099b4cdea9cSSteve Glendinning netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags);
210016c79a04SSteve Glendinning
2101b4cdea9cSSteve Glendinning /* do this first to ensure it's cleared even in error case */
2102b4cdea9cSSteve Glendinning pdata->suspend_flags = 0;
21036c636503SSteve Glendinning
2104b4cdea9cSSteve Glendinning if (suspend_flags & SUSPEND_ALLMODES) {
2105899a391bSSteve Glendinning /* Disable wakeup sources */
210647bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
2107e3c678e6SSteve Glendinning if (ret < 0) {
2108e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading WUCSR\n");
2109e3c678e6SSteve Glendinning return ret;
2110e3c678e6SSteve Glendinning }
21116c636503SSteve Glendinning
2112899a391bSSteve Glendinning val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN
2113899a391bSSteve Glendinning | WUCSR_BCST_EN);
21146c636503SSteve Glendinning
211547bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
2116e3c678e6SSteve Glendinning if (ret < 0) {
2117e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing WUCSR\n");
2118e3c678e6SSteve Glendinning return ret;
2119e3c678e6SSteve Glendinning }
21206c636503SSteve Glendinning
21216c636503SSteve Glendinning /* clear wake-up status */
212247bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
2123e3c678e6SSteve Glendinning if (ret < 0) {
2124e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
2125e3c678e6SSteve Glendinning return ret;
2126e3c678e6SSteve Glendinning }
21276c636503SSteve Glendinning
21286c636503SSteve Glendinning val &= ~PMT_CTL_WOL_EN;
21296c636503SSteve Glendinning val |= PMT_CTL_WUPS;
21306c636503SSteve Glendinning
213147bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
2132e3c678e6SSteve Glendinning if (ret < 0) {
2133e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
2134e3c678e6SSteve Glendinning return ret;
2135e3c678e6SSteve Glendinning }
2136b4cdea9cSSteve Glendinning }
2137b4cdea9cSSteve Glendinning
2138b4cdea9cSSteve Glendinning if (suspend_flags & SUSPEND_SUSPEND2) {
21391e1d7412SJoe Perches netdev_info(dev->net, "resuming from SUSPEND2\n");
214016c79a04SSteve Glendinning
214147bbea41SMing Lei ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
2142e3c678e6SSteve Glendinning if (ret < 0) {
2143e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error reading PMT_CTL\n");
2144e3c678e6SSteve Glendinning return ret;
2145e3c678e6SSteve Glendinning }
214616c79a04SSteve Glendinning
214716c79a04SSteve Glendinning val |= PMT_CTL_PHY_PWRUP;
214816c79a04SSteve Glendinning
214947bbea41SMing Lei ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
2150e3c678e6SSteve Glendinning if (ret < 0) {
2151e3c678e6SSteve Glendinning netdev_warn(dev->net, "Error writing PMT_CTL\n");
2152e3c678e6SSteve Glendinning return ret;
2153e3c678e6SSteve Glendinning }
21546c636503SSteve Glendinning }
215516c79a04SSteve Glendinning
215647bbea41SMing Lei ret = smsc75xx_wait_ready(dev, 1);
2157e3c678e6SSteve Glendinning if (ret < 0) {
2158e3c678e6SSteve Glendinning netdev_warn(dev->net, "device not ready in smsc75xx_resume\n");
2159e3c678e6SSteve Glendinning return ret;
2160e3c678e6SSteve Glendinning }
216116c79a04SSteve Glendinning
216216c79a04SSteve Glendinning return usbnet_resume(intf);
216316c79a04SSteve Glendinning }
216416c79a04SSteve Glendinning
smsc75xx_rx_csum_offload(struct usbnet * dev,struct sk_buff * skb,u32 rx_cmd_a,u32 rx_cmd_b)216578e47fe4SMichał Mirosław static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb,
216678e47fe4SMichał Mirosław u32 rx_cmd_a, u32 rx_cmd_b)
2167d0cad871SSteve Glendinning {
216878e47fe4SMichał Mirosław if (!(dev->net->features & NETIF_F_RXCSUM) ||
216978e47fe4SMichał Mirosław unlikely(rx_cmd_a & RX_CMD_A_LCSM)) {
2170d0cad871SSteve Glendinning skb->ip_summed = CHECKSUM_NONE;
2171d0cad871SSteve Glendinning } else {
2172d0cad871SSteve Glendinning skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT));
2173d0cad871SSteve Glendinning skb->ip_summed = CHECKSUM_COMPLETE;
2174d0cad871SSteve Glendinning }
2175d0cad871SSteve Glendinning }
2176d0cad871SSteve Glendinning
smsc75xx_rx_fixup(struct usbnet * dev,struct sk_buff * skb)2177d0cad871SSteve Glendinning static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
2178d0cad871SSteve Glendinning {
2179eb85569fSEmil Goode /* This check is no longer done by usbnet */
2180eb85569fSEmil Goode if (skb->len < dev->net->hard_header_len)
2181eb85569fSEmil Goode return 0;
2182eb85569fSEmil Goode
2183d0cad871SSteve Glendinning while (skb->len > 0) {
2184d0cad871SSteve Glendinning u32 rx_cmd_a, rx_cmd_b, align_count, size;
2185d0cad871SSteve Glendinning struct sk_buff *ax_skb;
2186d0cad871SSteve Glendinning unsigned char *packet;
2187d0cad871SSteve Glendinning
21885864118bSChuhong Yuan rx_cmd_a = get_unaligned_le32(skb->data);
2189d0cad871SSteve Glendinning skb_pull(skb, 4);
2190d0cad871SSteve Glendinning
21915864118bSChuhong Yuan rx_cmd_b = get_unaligned_le32(skb->data);
2192ea1649deSNico Erfurth skb_pull(skb, 4 + RXW_PADDING);
2193d0cad871SSteve Glendinning
2194d0cad871SSteve Glendinning packet = skb->data;
2195d0cad871SSteve Glendinning
2196d0cad871SSteve Glendinning /* get the packet length */
2197ea1649deSNico Erfurth size = (rx_cmd_a & RX_CMD_A_LEN) - RXW_PADDING;
2198ea1649deSNico Erfurth align_count = (4 - ((size + RXW_PADDING) % 4)) % 4;
2199d0cad871SSteve Glendinning
220043ffe6caSSzymon Heidrich if (unlikely(size > skb->len)) {
220143ffe6caSSzymon Heidrich netif_dbg(dev, rx_err, dev->net,
220243ffe6caSSzymon Heidrich "size err rx_cmd_a=0x%08x\n",
220343ffe6caSSzymon Heidrich rx_cmd_a);
220443ffe6caSSzymon Heidrich return 0;
220543ffe6caSSzymon Heidrich }
220643ffe6caSSzymon Heidrich
2207d0cad871SSteve Glendinning if (unlikely(rx_cmd_a & RX_CMD_A_RED)) {
2208d0cad871SSteve Glendinning netif_dbg(dev, rx_err, dev->net,
22091e1d7412SJoe Perches "Error rx_cmd_a=0x%08x\n", rx_cmd_a);
2210d0cad871SSteve Glendinning dev->net->stats.rx_errors++;
2211d0cad871SSteve Glendinning dev->net->stats.rx_dropped++;
2212d0cad871SSteve Glendinning
2213d0cad871SSteve Glendinning if (rx_cmd_a & RX_CMD_A_FCS)
2214d0cad871SSteve Glendinning dev->net->stats.rx_crc_errors++;
2215d0cad871SSteve Glendinning else if (rx_cmd_a & (RX_CMD_A_LONG | RX_CMD_A_RUNT))
2216d0cad871SSteve Glendinning dev->net->stats.rx_frame_errors++;
2217d0cad871SSteve Glendinning } else {
22184c51e536SSteve Glendinning /* MAX_SINGLE_PACKET_SIZE + 4(CRC) + 2(COE) + 4(Vlan) */
221943ffe6caSSzymon Heidrich if (unlikely(size > (MAX_SINGLE_PACKET_SIZE + ETH_HLEN + 12))) {
2220d0cad871SSteve Glendinning netif_dbg(dev, rx_err, dev->net,
22211e1d7412SJoe Perches "size err rx_cmd_a=0x%08x\n",
22221e1d7412SJoe Perches rx_cmd_a);
2223d0cad871SSteve Glendinning return 0;
2224d0cad871SSteve Glendinning }
2225d0cad871SSteve Glendinning
2226d0cad871SSteve Glendinning /* last frame in this batch */
2227d0cad871SSteve Glendinning if (skb->len == size) {
222878e47fe4SMichał Mirosław smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a,
2229d0cad871SSteve Glendinning rx_cmd_b);
2230d0cad871SSteve Glendinning
2231d0cad871SSteve Glendinning skb_trim(skb, skb->len - 4); /* remove fcs */
2232d0cad871SSteve Glendinning
2233d0cad871SSteve Glendinning return 1;
2234d0cad871SSteve Glendinning }
2235d0cad871SSteve Glendinning
2236*1b3b2d9eSEric Dumazet /* Use "size - 4" to remove fcs */
2237*1b3b2d9eSEric Dumazet ax_skb = netdev_alloc_skb_ip_align(dev->net, size - 4);
2238d0cad871SSteve Glendinning if (unlikely(!ax_skb)) {
22391e1d7412SJoe Perches netdev_warn(dev->net, "Error allocating skb\n");
2240d0cad871SSteve Glendinning return 0;
2241d0cad871SSteve Glendinning }
2242d0cad871SSteve Glendinning
2243*1b3b2d9eSEric Dumazet skb_put(ax_skb, size - 4);
2244*1b3b2d9eSEric Dumazet memcpy(ax_skb->data, packet, size - 4);
2245d0cad871SSteve Glendinning
224678e47fe4SMichał Mirosław smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a,
2247d0cad871SSteve Glendinning rx_cmd_b);
2248d0cad871SSteve Glendinning
2249d0cad871SSteve Glendinning usbnet_skb_return(dev, ax_skb);
2250d0cad871SSteve Glendinning }
2251d0cad871SSteve Glendinning
2252d0cad871SSteve Glendinning skb_pull(skb, size);
2253d0cad871SSteve Glendinning
2254d0cad871SSteve Glendinning /* padding bytes before the next frame starts */
2255d0cad871SSteve Glendinning if (skb->len)
2256d0cad871SSteve Glendinning skb_pull(skb, align_count);
2257d0cad871SSteve Glendinning }
2258d0cad871SSteve Glendinning
2259d0cad871SSteve Glendinning return 1;
2260d0cad871SSteve Glendinning }
2261d0cad871SSteve Glendinning
smsc75xx_tx_fixup(struct usbnet * dev,struct sk_buff * skb,gfp_t flags)2262d0cad871SSteve Glendinning static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev,
2263d0cad871SSteve Glendinning struct sk_buff *skb, gfp_t flags)
2264d0cad871SSteve Glendinning {
2265d0cad871SSteve Glendinning u32 tx_cmd_a, tx_cmd_b;
22667e24b4edSChuhong Yuan void *ptr;
2267d0cad871SSteve Glendinning
2268b7c6d267SEric Dumazet if (skb_cow_head(skb, SMSC75XX_TX_OVERHEAD)) {
2269d0cad871SSteve Glendinning dev_kfree_skb_any(skb);
2270d0cad871SSteve Glendinning return NULL;
2271d0cad871SSteve Glendinning }
2272d0cad871SSteve Glendinning
2273d0cad871SSteve Glendinning tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS;
2274d0cad871SSteve Glendinning
2275d0cad871SSteve Glendinning if (skb->ip_summed == CHECKSUM_PARTIAL)
2276d0cad871SSteve Glendinning tx_cmd_a |= TX_CMD_A_IPE | TX_CMD_A_TPE;
2277d0cad871SSteve Glendinning
2278d0cad871SSteve Glendinning if (skb_is_gso(skb)) {
2279d0cad871SSteve Glendinning u16 mss = max(skb_shinfo(skb)->gso_size, TX_MSS_MIN);
2280d0cad871SSteve Glendinning tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT) & TX_CMD_B_MSS;
2281d0cad871SSteve Glendinning
2282d0cad871SSteve Glendinning tx_cmd_a |= TX_CMD_A_LSO;
2283d0cad871SSteve Glendinning } else {
2284d0cad871SSteve Glendinning tx_cmd_b = 0;
2285d0cad871SSteve Glendinning }
2286d0cad871SSteve Glendinning
22877e24b4edSChuhong Yuan ptr = skb_push(skb, 8);
22887e24b4edSChuhong Yuan put_unaligned_le32(tx_cmd_a, ptr);
22897e24b4edSChuhong Yuan put_unaligned_le32(tx_cmd_b, ptr + 4);
2290d0cad871SSteve Glendinning
2291d0cad871SSteve Glendinning return skb;
2292d0cad871SSteve Glendinning }
2293d0cad871SSteve Glendinning
smsc75xx_manage_power(struct usbnet * dev,int on)2294b4cdea9cSSteve Glendinning static int smsc75xx_manage_power(struct usbnet *dev, int on)
2295b4cdea9cSSteve Glendinning {
2296b4cdea9cSSteve Glendinning dev->intf->needs_remote_wakeup = on;
2297b4cdea9cSSteve Glendinning return 0;
2298b4cdea9cSSteve Glendinning }
2299b4cdea9cSSteve Glendinning
2300d0cad871SSteve Glendinning static const struct driver_info smsc75xx_info = {
2301d0cad871SSteve Glendinning .description = "smsc75xx USB 2.0 Gigabit Ethernet",
2302d0cad871SSteve Glendinning .bind = smsc75xx_bind,
2303d0cad871SSteve Glendinning .unbind = smsc75xx_unbind,
2304d0cad871SSteve Glendinning .link_reset = smsc75xx_link_reset,
2305d0cad871SSteve Glendinning .reset = smsc75xx_reset,
2306d0cad871SSteve Glendinning .rx_fixup = smsc75xx_rx_fixup,
2307d0cad871SSteve Glendinning .tx_fixup = smsc75xx_tx_fixup,
2308d0cad871SSteve Glendinning .status = smsc75xx_status,
2309b4cdea9cSSteve Glendinning .manage_power = smsc75xx_manage_power,
23107bdd305eSSteve Glendinning .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,
2311d0cad871SSteve Glendinning };
2312d0cad871SSteve Glendinning
2313d0cad871SSteve Glendinning static const struct usb_device_id products[] = {
2314d0cad871SSteve Glendinning {
2315d0cad871SSteve Glendinning /* SMSC7500 USB Gigabit Ethernet Device */
2316d0cad871SSteve Glendinning USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7500),
2317d0cad871SSteve Glendinning .driver_info = (unsigned long) &smsc75xx_info,
2318d0cad871SSteve Glendinning },
2319d0cad871SSteve Glendinning {
2320d0cad871SSteve Glendinning /* SMSC7500 USB Gigabit Ethernet Device */
2321d0cad871SSteve Glendinning USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7505),
2322d0cad871SSteve Glendinning .driver_info = (unsigned long) &smsc75xx_info,
2323d0cad871SSteve Glendinning },
2324d0cad871SSteve Glendinning { }, /* END */
2325d0cad871SSteve Glendinning };
2326d0cad871SSteve Glendinning MODULE_DEVICE_TABLE(usb, products);
2327d0cad871SSteve Glendinning
2328d0cad871SSteve Glendinning static struct usb_driver smsc75xx_driver = {
2329d0cad871SSteve Glendinning .name = SMSC_CHIPNAME,
2330d0cad871SSteve Glendinning .id_table = products,
2331d0cad871SSteve Glendinning .probe = usbnet_probe,
233216c79a04SSteve Glendinning .suspend = smsc75xx_suspend,
233316c79a04SSteve Glendinning .resume = smsc75xx_resume,
233416c79a04SSteve Glendinning .reset_resume = smsc75xx_resume,
2335d0cad871SSteve Glendinning .disconnect = usbnet_disconnect,
2336e1f12eb6SSarah Sharp .disable_hub_initiated_lpm = 1,
2337b4cdea9cSSteve Glendinning .supports_autosuspend = 1,
2338d0cad871SSteve Glendinning };
2339d0cad871SSteve Glendinning
2340d632eb1bSGreg Kroah-Hartman module_usb_driver(smsc75xx_driver);
2341d0cad871SSteve Glendinning
2342d0cad871SSteve Glendinning MODULE_AUTHOR("Nancy Lin");
234390b24cfbSSteve Glendinning MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");
2344d0cad871SSteve Glendinning MODULE_DESCRIPTION("SMSC75XX USB 2.0 Gigabit Ethernet Devices");
2345d0cad871SSteve Glendinning MODULE_LICENSE("GPL");
2346