xref: /openbmc/linux/drivers/net/usb/dm9601.c (revision 816ffd28002651a469e86d1118a225862e392ecd)
15b2fc499SJeff Garzik /*
2cabd0e3aSPeter Korsgaard  * Davicom DM96xx USB 10/100Mbps ethernet devices
35b2fc499SJeff Garzik  *
45b2fc499SJeff Garzik  * Peter Korsgaard <jacmet@sunsite.dk>
55b2fc499SJeff Garzik  *
65b2fc499SJeff Garzik  * This file is licensed under the terms of the GNU General Public License
75b2fc499SJeff Garzik  * version 2.  This program is licensed "as is" without any warranty of any
85b2fc499SJeff Garzik  * kind, whether express or implied.
95b2fc499SJeff Garzik  */
105b2fc499SJeff Garzik 
115b2fc499SJeff Garzik //#define DEBUG
125b2fc499SJeff Garzik 
135b2fc499SJeff Garzik #include <linux/module.h>
145b2fc499SJeff Garzik #include <linux/sched.h>
155b2fc499SJeff Garzik #include <linux/stddef.h>
165b2fc499SJeff Garzik #include <linux/netdevice.h>
175b2fc499SJeff Garzik #include <linux/etherdevice.h>
185b2fc499SJeff Garzik #include <linux/ethtool.h>
195b2fc499SJeff Garzik #include <linux/mii.h>
205b2fc499SJeff Garzik #include <linux/usb.h>
215b2fc499SJeff Garzik #include <linux/crc32.h>
223692e94fSJussi Kivilinna #include <linux/usb/usbnet.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
245b2fc499SJeff Garzik 
255b2fc499SJeff Garzik /* datasheet:
2698658bc9SWu Fengguang  http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf
275b2fc499SJeff Garzik */
285b2fc499SJeff Garzik 
295b2fc499SJeff Garzik /* control requests */
305b2fc499SJeff Garzik #define DM_READ_REGS	0x00
315b2fc499SJeff Garzik #define DM_WRITE_REGS	0x01
325b2fc499SJeff Garzik #define DM_READ_MEMS	0x02
335b2fc499SJeff Garzik #define DM_WRITE_REG	0x03
345b2fc499SJeff Garzik #define DM_WRITE_MEMS	0x05
355b2fc499SJeff Garzik #define DM_WRITE_MEM	0x07
365b2fc499SJeff Garzik 
375b2fc499SJeff Garzik /* registers */
385b2fc499SJeff Garzik #define DM_NET_CTRL	0x00
395b2fc499SJeff Garzik #define DM_RX_CTRL	0x05
405b2fc499SJeff Garzik #define DM_SHARED_CTRL	0x0b
415b2fc499SJeff Garzik #define DM_SHARED_ADDR	0x0c
425b2fc499SJeff Garzik #define DM_SHARED_DATA	0x0d	/* low + high */
435b2fc499SJeff Garzik #define DM_PHY_ADDR	0x10	/* 6 bytes */
445b2fc499SJeff Garzik #define DM_MCAST_ADDR	0x16	/* 8 bytes */
455b2fc499SJeff Garzik #define DM_GPR_CTRL	0x1e
465b2fc499SJeff Garzik #define DM_GPR_DATA	0x1f
476642f91cSPeter Korsgaard #define DM_CHIP_ID	0x2c
486642f91cSPeter Korsgaard #define DM_MODE_CTRL	0x91	/* only on dm9620 */
496642f91cSPeter Korsgaard 
506642f91cSPeter Korsgaard /* chip id values */
516642f91cSPeter Korsgaard #define ID_DM9601	0
526642f91cSPeter Korsgaard #define ID_DM9620	1
535b2fc499SJeff Garzik 
545b2fc499SJeff Garzik #define DM_MAX_MCAST	64
555b2fc499SJeff Garzik #define DM_MCAST_SIZE	8
565b2fc499SJeff Garzik #define DM_EEPROM_LEN	256
575b2fc499SJeff Garzik #define DM_TX_OVERHEAD	2	/* 2 byte header */
585b2fc499SJeff Garzik #define DM_RX_OVERHEAD	7	/* 3 byte header + 4 byte crc tail */
595b2fc499SJeff Garzik #define DM_TIMEOUT	1000
605b2fc499SJeff Garzik 
dm_read(struct usbnet * dev,u8 reg,u16 length,void * data)615b2fc499SJeff Garzik static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
625b2fc499SJeff Garzik {
6324b1042cSMing Lei 	int err;
6424b1042cSMing Lei 	err = usbnet_read_cmd(dev, DM_READ_REGS,
655b2fc499SJeff Garzik 			       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
6624b1042cSMing Lei 			       0, reg, data, length);
6724b1042cSMing Lei 	if(err != length && err >= 0)
6816d78bc2SPeter Korsgaard 		err = -EINVAL;
6916d78bc2SPeter Korsgaard 	return err;
705b2fc499SJeff Garzik }
715b2fc499SJeff Garzik 
dm_read_reg(struct usbnet * dev,u8 reg,u8 * value)725b2fc499SJeff Garzik static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
735b2fc499SJeff Garzik {
745b2fc499SJeff Garzik 	return dm_read(dev, reg, 1, value);
755b2fc499SJeff Garzik }
765b2fc499SJeff Garzik 
dm_write(struct usbnet * dev,u8 reg,u16 length,void * data)775b2fc499SJeff Garzik static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
785b2fc499SJeff Garzik {
7924b1042cSMing Lei 	int err;
8024b1042cSMing Lei 	err = usbnet_write_cmd(dev, DM_WRITE_REGS,
815b2fc499SJeff Garzik 				USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
8224b1042cSMing Lei 				0, reg, data, length);
8324b1042cSMing Lei 
8416d78bc2SPeter Korsgaard 	if (err >= 0 && err < length)
8516d78bc2SPeter Korsgaard 		err = -EINVAL;
8616d78bc2SPeter Korsgaard 	return err;
875b2fc499SJeff Garzik }
885b2fc499SJeff Garzik 
dm_write_reg(struct usbnet * dev,u8 reg,u8 value)895b2fc499SJeff Garzik static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
905b2fc499SJeff Garzik {
911b9c3a1bSTushar Behera 	return usbnet_write_cmd(dev, DM_WRITE_REG,
925b2fc499SJeff Garzik 				USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
9324b1042cSMing Lei 				value, reg, NULL, 0);
945b2fc499SJeff Garzik }
955b2fc499SJeff Garzik 
dm_write_async(struct usbnet * dev,u8 reg,u16 length,const void * data)9676660757SJakub Kicinski static void dm_write_async(struct usbnet *dev, u8 reg, u16 length,
9776660757SJakub Kicinski 			   const void *data)
985b2fc499SJeff Garzik {
9924b1042cSMing Lei 	usbnet_write_cmd_async(dev, DM_WRITE_REGS,
10024b1042cSMing Lei 			       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1011b9c3a1bSTushar Behera 			       0, reg, data, length);
102ba734f34SPeter Korsgaard }
103ba734f34SPeter Korsgaard 
dm_write_reg_async(struct usbnet * dev,u8 reg,u8 value)1045b2fc499SJeff Garzik static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
1055b2fc499SJeff Garzik {
1061b9c3a1bSTushar Behera 	usbnet_write_cmd_async(dev, DM_WRITE_REG,
1071b9c3a1bSTushar Behera 			       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1081b9c3a1bSTushar Behera 			       value, reg, NULL, 0);
1095b2fc499SJeff Garzik }
1105b2fc499SJeff Garzik 
dm_read_shared_word(struct usbnet * dev,int phy,u8 reg,__le16 * value)111eca1ad82SAl Viro static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value)
1125b2fc499SJeff Garzik {
1135b2fc499SJeff Garzik 	int ret, i;
1145b2fc499SJeff Garzik 
1155b2fc499SJeff Garzik 	mutex_lock(&dev->phy_mutex);
1165b2fc499SJeff Garzik 
1175b2fc499SJeff Garzik 	dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
1185b2fc499SJeff Garzik 	dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4);
1195b2fc499SJeff Garzik 
1205b2fc499SJeff Garzik 	for (i = 0; i < DM_TIMEOUT; i++) {
12115c8bb12SSimon Que 		u8 tmp = 0;
1225b2fc499SJeff Garzik 
1235b2fc499SJeff Garzik 		udelay(1);
1245b2fc499SJeff Garzik 		ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
1255b2fc499SJeff Garzik 		if (ret < 0)
1265b2fc499SJeff Garzik 			goto out;
1275b2fc499SJeff Garzik 
1285b2fc499SJeff Garzik 		/* ready */
1295b2fc499SJeff Garzik 		if ((tmp & 1) == 0)
1305b2fc499SJeff Garzik 			break;
1315b2fc499SJeff Garzik 	}
1325b2fc499SJeff Garzik 
1335b2fc499SJeff Garzik 	if (i == DM_TIMEOUT) {
13460b86755SJoe Perches 		netdev_err(dev->net, "%s read timed out!\n", phy ? "phy" : "eeprom");
1355b2fc499SJeff Garzik 		ret = -EIO;
1365b2fc499SJeff Garzik 		goto out;
1375b2fc499SJeff Garzik 	}
1385b2fc499SJeff Garzik 
1395b2fc499SJeff Garzik 	dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
1405b2fc499SJeff Garzik 	ret = dm_read(dev, DM_SHARED_DATA, 2, value);
1415b2fc499SJeff Garzik 
14260b86755SJoe Perches 	netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n",
1435b2fc499SJeff Garzik 		   phy, reg, *value, ret);
1445b2fc499SJeff Garzik 
1455b2fc499SJeff Garzik  out:
1465b2fc499SJeff Garzik 	mutex_unlock(&dev->phy_mutex);
1475b2fc499SJeff Garzik 	return ret;
1485b2fc499SJeff Garzik }
1495b2fc499SJeff Garzik 
dm_write_shared_word(struct usbnet * dev,int phy,u8 reg,__le16 value)150eca1ad82SAl Viro static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 value)
1515b2fc499SJeff Garzik {
1525b2fc499SJeff Garzik 	int ret, i;
1535b2fc499SJeff Garzik 
1545b2fc499SJeff Garzik 	mutex_lock(&dev->phy_mutex);
1555b2fc499SJeff Garzik 
1565b2fc499SJeff Garzik 	ret = dm_write(dev, DM_SHARED_DATA, 2, &value);
1575b2fc499SJeff Garzik 	if (ret < 0)
1585b2fc499SJeff Garzik 		goto out;
1595b2fc499SJeff Garzik 
1605b2fc499SJeff Garzik 	dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
161e9162ab1SPeter Korsgaard 	dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1a : 0x12);
1625b2fc499SJeff Garzik 
1635b2fc499SJeff Garzik 	for (i = 0; i < DM_TIMEOUT; i++) {
16415c8bb12SSimon Que 		u8 tmp = 0;
1655b2fc499SJeff Garzik 
1665b2fc499SJeff Garzik 		udelay(1);
1675b2fc499SJeff Garzik 		ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
1685b2fc499SJeff Garzik 		if (ret < 0)
1695b2fc499SJeff Garzik 			goto out;
1705b2fc499SJeff Garzik 
1715b2fc499SJeff Garzik 		/* ready */
1725b2fc499SJeff Garzik 		if ((tmp & 1) == 0)
1735b2fc499SJeff Garzik 			break;
1745b2fc499SJeff Garzik 	}
1755b2fc499SJeff Garzik 
1765b2fc499SJeff Garzik 	if (i == DM_TIMEOUT) {
17760b86755SJoe Perches 		netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom");
1785b2fc499SJeff Garzik 		ret = -EIO;
1795b2fc499SJeff Garzik 		goto out;
1805b2fc499SJeff Garzik 	}
1815b2fc499SJeff Garzik 
1825b2fc499SJeff Garzik 	dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
1835b2fc499SJeff Garzik 
1845b2fc499SJeff Garzik out:
1855b2fc499SJeff Garzik 	mutex_unlock(&dev->phy_mutex);
1865b2fc499SJeff Garzik 	return ret;
1875b2fc499SJeff Garzik }
1885b2fc499SJeff Garzik 
dm_read_eeprom_word(struct usbnet * dev,u8 offset,void * value)1895b2fc499SJeff Garzik static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
1905b2fc499SJeff Garzik {
1915b2fc499SJeff Garzik 	return dm_read_shared_word(dev, 0, offset, value);
1925b2fc499SJeff Garzik }
1935b2fc499SJeff Garzik 
1945b2fc499SJeff Garzik 
1955b2fc499SJeff Garzik 
dm9601_get_eeprom_len(struct net_device * dev)1965b2fc499SJeff Garzik static int dm9601_get_eeprom_len(struct net_device *dev)
1975b2fc499SJeff Garzik {
1985b2fc499SJeff Garzik 	return DM_EEPROM_LEN;
1995b2fc499SJeff Garzik }
2005b2fc499SJeff Garzik 
dm9601_get_eeprom(struct net_device * net,struct ethtool_eeprom * eeprom,u8 * data)2015b2fc499SJeff Garzik static int dm9601_get_eeprom(struct net_device *net,
2025b2fc499SJeff Garzik 			     struct ethtool_eeprom *eeprom, u8 * data)
2035b2fc499SJeff Garzik {
2045b2fc499SJeff Garzik 	struct usbnet *dev = netdev_priv(net);
205eca1ad82SAl Viro 	__le16 *ebuf = (__le16 *) data;
2065b2fc499SJeff Garzik 	int i;
2075b2fc499SJeff Garzik 
2085b2fc499SJeff Garzik 	/* access is 16bit */
2095b2fc499SJeff Garzik 	if ((eeprom->offset % 2) || (eeprom->len % 2))
2105b2fc499SJeff Garzik 		return -EINVAL;
2115b2fc499SJeff Garzik 
2125b2fc499SJeff Garzik 	for (i = 0; i < eeprom->len / 2; i++) {
2135b2fc499SJeff Garzik 		if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i,
2145b2fc499SJeff Garzik 					&ebuf[i]) < 0)
2155b2fc499SJeff Garzik 			return -EINVAL;
2165b2fc499SJeff Garzik 	}
2175b2fc499SJeff Garzik 	return 0;
2185b2fc499SJeff Garzik }
2195b2fc499SJeff Garzik 
dm9601_mdio_read(struct net_device * netdev,int phy_id,int loc)2205b2fc499SJeff Garzik static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc)
2215b2fc499SJeff Garzik {
2225b2fc499SJeff Garzik 	struct usbnet *dev = netdev_priv(netdev);
2235b2fc499SJeff Garzik 
224eca1ad82SAl Viro 	__le16 res;
2258f8abb86SJavier Carrasco 	int err;
2265b2fc499SJeff Garzik 
2275b2fc499SJeff Garzik 	if (phy_id) {
22860b86755SJoe Perches 		netdev_dbg(dev->net, "Only internal phy supported\n");
2295b2fc499SJeff Garzik 		return 0;
2305b2fc499SJeff Garzik 	}
2315b2fc499SJeff Garzik 
2328f8abb86SJavier Carrasco 	err = dm_read_shared_word(dev, 1, loc, &res);
2338f8abb86SJavier Carrasco 	if (err < 0) {
2348f8abb86SJavier Carrasco 		netdev_err(dev->net, "MDIO read error: %d\n", err);
235*0ec3ea61SJavier Carrasco 		return 0;
2368f8abb86SJavier Carrasco 	}
2375b2fc499SJeff Garzik 
23860b86755SJoe Perches 	netdev_dbg(dev->net,
23960b86755SJoe Perches 		   "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
2405b2fc499SJeff Garzik 		   phy_id, loc, le16_to_cpu(res));
2415b2fc499SJeff Garzik 
2425b2fc499SJeff Garzik 	return le16_to_cpu(res);
2435b2fc499SJeff Garzik }
2445b2fc499SJeff Garzik 
dm9601_mdio_write(struct net_device * netdev,int phy_id,int loc,int val)2455b2fc499SJeff Garzik static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc,
2465b2fc499SJeff Garzik 			      int val)
2475b2fc499SJeff Garzik {
2485b2fc499SJeff Garzik 	struct usbnet *dev = netdev_priv(netdev);
249eca1ad82SAl Viro 	__le16 res = cpu_to_le16(val);
2505b2fc499SJeff Garzik 
2515b2fc499SJeff Garzik 	if (phy_id) {
25260b86755SJoe Perches 		netdev_dbg(dev->net, "Only internal phy supported\n");
2535b2fc499SJeff Garzik 		return;
2545b2fc499SJeff Garzik 	}
2555b2fc499SJeff Garzik 
25660b86755SJoe Perches 	netdev_dbg(dev->net, "dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
2575b2fc499SJeff Garzik 		   phy_id, loc, val);
2585b2fc499SJeff Garzik 
2595b2fc499SJeff Garzik 	dm_write_shared_word(dev, 1, loc, res);
2605b2fc499SJeff Garzik }
2615b2fc499SJeff Garzik 
dm9601_get_drvinfo(struct net_device * net,struct ethtool_drvinfo * info)2625b2fc499SJeff Garzik static void dm9601_get_drvinfo(struct net_device *net,
2635b2fc499SJeff Garzik 			       struct ethtool_drvinfo *info)
2645b2fc499SJeff Garzik {
2655b2fc499SJeff Garzik 	/* Inherit standard device info */
2665b2fc499SJeff Garzik 	usbnet_get_drvinfo(net, info);
2675b2fc499SJeff Garzik }
2685b2fc499SJeff Garzik 
dm9601_get_link(struct net_device * net)2695b2fc499SJeff Garzik static u32 dm9601_get_link(struct net_device *net)
2705b2fc499SJeff Garzik {
2715b2fc499SJeff Garzik 	struct usbnet *dev = netdev_priv(net);
2725b2fc499SJeff Garzik 
2735b2fc499SJeff Garzik 	return mii_link_ok(&dev->mii);
2745b2fc499SJeff Garzik }
2755b2fc499SJeff Garzik 
dm9601_ioctl(struct net_device * net,struct ifreq * rq,int cmd)2765b2fc499SJeff Garzik static int dm9601_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
2775b2fc499SJeff Garzik {
2785b2fc499SJeff Garzik 	struct usbnet *dev = netdev_priv(net);
2795b2fc499SJeff Garzik 
2805b2fc499SJeff Garzik 	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
2815b2fc499SJeff Garzik }
2825b2fc499SJeff Garzik 
2830fc0b732SStephen Hemminger static const struct ethtool_ops dm9601_ethtool_ops = {
2845b2fc499SJeff Garzik 	.get_drvinfo	= dm9601_get_drvinfo,
2855b2fc499SJeff Garzik 	.get_link	= dm9601_get_link,
2865b2fc499SJeff Garzik 	.get_msglevel	= usbnet_get_msglevel,
2875b2fc499SJeff Garzik 	.set_msglevel	= usbnet_set_msglevel,
2885b2fc499SJeff Garzik 	.get_eeprom_len	= dm9601_get_eeprom_len,
2895b2fc499SJeff Garzik 	.get_eeprom	= dm9601_get_eeprom,
2905b2fc499SJeff Garzik 	.nway_reset	= usbnet_nway_reset,
29177651900SOliver Neukum 	.get_link_ksettings	= usbnet_get_link_ksettings_mii,
29277651900SOliver Neukum 	.set_link_ksettings	= usbnet_set_link_ksettings_mii,
2935b2fc499SJeff Garzik };
2945b2fc499SJeff Garzik 
dm9601_set_multicast(struct net_device * net)2955b2fc499SJeff Garzik static void dm9601_set_multicast(struct net_device *net)
2965b2fc499SJeff Garzik {
2975b2fc499SJeff Garzik 	struct usbnet *dev = netdev_priv(net);
2985b2fc499SJeff Garzik 	/* We use the 20 byte dev->data for our 8 byte filter buffer
2995b2fc499SJeff Garzik 	 * to avoid allocating memory that is tricky to free later */
3005b2fc499SJeff Garzik 	u8 *hashes = (u8 *) & dev->data;
30133eddedbSPeter Korsgaard 	u8 rx_ctl = 0x31;
3025b2fc499SJeff Garzik 
3035b2fc499SJeff Garzik 	memset(hashes, 0x00, DM_MCAST_SIZE);
3045b2fc499SJeff Garzik 	hashes[DM_MCAST_SIZE - 1] |= 0x80;	/* broadcast address */
3055b2fc499SJeff Garzik 
3065b2fc499SJeff Garzik 	if (net->flags & IFF_PROMISC) {
3075b2fc499SJeff Garzik 		rx_ctl |= 0x02;
3082cc04d27SJiri Pirko 	} else if (net->flags & IFF_ALLMULTI ||
3092cc04d27SJiri Pirko 		   netdev_mc_count(net) > DM_MAX_MCAST) {
310bf0ea638SPeter Korsgaard 		rx_ctl |= 0x08;
3112cc04d27SJiri Pirko 	} else if (!netdev_mc_empty(net)) {
31222bedad3SJiri Pirko 		struct netdev_hw_addr *ha;
3135b2fc499SJeff Garzik 
31422bedad3SJiri Pirko 		netdev_for_each_mc_addr(ha, net) {
31522bedad3SJiri Pirko 			u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26;
3165b2fc499SJeff Garzik 			hashes[crc >> 3] |= 1 << (crc & 0x7);
3175b2fc499SJeff Garzik 		}
3185b2fc499SJeff Garzik 	}
3195b2fc499SJeff Garzik 
3205b2fc499SJeff Garzik 	dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes);
3215b2fc499SJeff Garzik 	dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl);
3225b2fc499SJeff Garzik }
3235b2fc499SJeff Garzik 
__dm9601_set_mac_address(struct usbnet * dev)3244b9f8ec6SWu Fengguang static void __dm9601_set_mac_address(struct usbnet *dev)
3254b9f8ec6SWu Fengguang {
3264b9f8ec6SWu Fengguang 	dm_write_async(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr);
3274b9f8ec6SWu Fengguang }
3284b9f8ec6SWu Fengguang 
dm9601_set_mac_address(struct net_device * net,void * p)329753dcfeeSPeter Korsgaard static int dm9601_set_mac_address(struct net_device *net, void *p)
330753dcfeeSPeter Korsgaard {
331753dcfeeSPeter Korsgaard 	struct sockaddr *addr = p;
332753dcfeeSPeter Korsgaard 	struct usbnet *dev = netdev_priv(net);
333753dcfeeSPeter Korsgaard 
334f52deb0eSWu Fengguang 	if (!is_valid_ether_addr(addr->sa_data)) {
335f52deb0eSWu Fengguang 		dev_err(&net->dev, "not setting invalid mac address %pM\n",
336f52deb0eSWu Fengguang 								addr->sa_data);
337753dcfeeSPeter Korsgaard 		return -EINVAL;
338f52deb0eSWu Fengguang 	}
339753dcfeeSPeter Korsgaard 
34049ed8ddeSJakub Kicinski 	eth_hw_addr_set(net, addr->sa_data);
3414b9f8ec6SWu Fengguang 	__dm9601_set_mac_address(dev);
342753dcfeeSPeter Korsgaard 
343753dcfeeSPeter Korsgaard 	return 0;
344753dcfeeSPeter Korsgaard }
345753dcfeeSPeter Korsgaard 
346fe85ff82SStephen Hemminger static const struct net_device_ops dm9601_netdev_ops = {
347fe85ff82SStephen Hemminger 	.ndo_open		= usbnet_open,
348fe85ff82SStephen Hemminger 	.ndo_stop		= usbnet_stop,
349fe85ff82SStephen Hemminger 	.ndo_start_xmit		= usbnet_start_xmit,
350fe85ff82SStephen Hemminger 	.ndo_tx_timeout		= usbnet_tx_timeout,
351fe85ff82SStephen Hemminger 	.ndo_change_mtu		= usbnet_change_mtu,
352323955a0SHeiner Kallweit 	.ndo_get_stats64	= dev_get_tstats64,
353fe85ff82SStephen Hemminger 	.ndo_validate_addr	= eth_validate_addr,
354a7605370SArnd Bergmann 	.ndo_eth_ioctl		= dm9601_ioctl,
355afc4b13dSJiri Pirko 	.ndo_set_rx_mode	= dm9601_set_multicast,
356fe85ff82SStephen Hemminger 	.ndo_set_mac_address	= dm9601_set_mac_address,
357fe85ff82SStephen Hemminger };
358fe85ff82SStephen Hemminger 
dm9601_bind(struct usbnet * dev,struct usb_interface * intf)3595b2fc499SJeff Garzik static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
3605b2fc499SJeff Garzik {
3615b2fc499SJeff Garzik 	int ret;
3626642f91cSPeter Korsgaard 	u8 mac[ETH_ALEN], id;
3635b2fc499SJeff Garzik 
3645b2fc499SJeff Garzik 	ret = usbnet_get_endpoints(dev, intf);
3655b2fc499SJeff Garzik 	if (ret)
3665b2fc499SJeff Garzik 		goto out;
3675b2fc499SJeff Garzik 
368fe85ff82SStephen Hemminger 	dev->net->netdev_ops = &dm9601_netdev_ops;
3695b2fc499SJeff Garzik 	dev->net->ethtool_ops = &dm9601_ethtool_ops;
3705b2fc499SJeff Garzik 	dev->net->hard_header_len += DM_TX_OVERHEAD;
3715b2fc499SJeff Garzik 	dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
372407900cfSPeter Korsgaard 
373407900cfSPeter Korsgaard 	/* dm9620/21a require room for 4 byte padding, even in dm9601
374407900cfSPeter Korsgaard 	 * mode, so we need +1 to be able to receive full size
375407900cfSPeter Korsgaard 	 * ethernet frames.
376407900cfSPeter Korsgaard 	 */
377407900cfSPeter Korsgaard 	dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD + 1;
3785b2fc499SJeff Garzik 
3795b2fc499SJeff Garzik 	dev->mii.dev = dev->net;
3805b2fc499SJeff Garzik 	dev->mii.mdio_read = dm9601_mdio_read;
3815b2fc499SJeff Garzik 	dev->mii.mdio_write = dm9601_mdio_write;
3825b2fc499SJeff Garzik 	dev->mii.phy_id_mask = 0x1f;
3835b2fc499SJeff Garzik 	dev->mii.reg_num_mask = 0x1f;
3845b2fc499SJeff Garzik 
3855b2fc499SJeff Garzik 	/* reset */
386b8f59586SPeter Korsgaard 	dm_write_reg(dev, DM_NET_CTRL, 1);
3875b2fc499SJeff Garzik 	udelay(20);
3885b2fc499SJeff Garzik 
3895b2fc499SJeff Garzik 	/* read MAC */
39020f10aa0SWu Fengguang 	if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, mac) < 0) {
3915b2fc499SJeff Garzik 		printk(KERN_ERR "Error reading MAC address\n");
3925b2fc499SJeff Garzik 		ret = -ENODEV;
3935b2fc499SJeff Garzik 		goto out;
3945b2fc499SJeff Garzik 	}
3955b2fc499SJeff Garzik 
39620f10aa0SWu Fengguang 	/*
39720f10aa0SWu Fengguang 	 * Overwrite the auto-generated address only with good ones.
39820f10aa0SWu Fengguang 	 */
39920f10aa0SWu Fengguang 	if (is_valid_ether_addr(mac))
40016813717SJakub Kicinski 		eth_hw_addr_set(dev->net, mac);
401f52deb0eSWu Fengguang 	else {
402f52deb0eSWu Fengguang 		printk(KERN_WARNING
403f52deb0eSWu Fengguang 			"dm9601: No valid MAC address in EEPROM, using %pM\n",
404f52deb0eSWu Fengguang 			dev->net->dev_addr);
4054b9f8ec6SWu Fengguang 		__dm9601_set_mac_address(dev);
406f52deb0eSWu Fengguang 	}
40720f10aa0SWu Fengguang 
4086642f91cSPeter Korsgaard 	if (dm_read_reg(dev, DM_CHIP_ID, &id) < 0) {
4096642f91cSPeter Korsgaard 		netdev_err(dev->net, "Error reading chip ID\n");
4106642f91cSPeter Korsgaard 		ret = -ENODEV;
4116642f91cSPeter Korsgaard 		goto out;
4126642f91cSPeter Korsgaard 	}
4136642f91cSPeter Korsgaard 
4146642f91cSPeter Korsgaard 	/* put dm9620 devices in dm9601 mode */
4156642f91cSPeter Korsgaard 	if (id == ID_DM9620) {
4166642f91cSPeter Korsgaard 		u8 mode;
4176642f91cSPeter Korsgaard 
4186642f91cSPeter Korsgaard 		if (dm_read_reg(dev, DM_MODE_CTRL, &mode) < 0) {
4196642f91cSPeter Korsgaard 			netdev_err(dev->net, "Error reading MODE_CTRL\n");
4206642f91cSPeter Korsgaard 			ret = -ENODEV;
4216642f91cSPeter Korsgaard 			goto out;
4226642f91cSPeter Korsgaard 		}
4236642f91cSPeter Korsgaard 		dm_write_reg(dev, DM_MODE_CTRL, mode & 0x7f);
4246642f91cSPeter Korsgaard 	}
4256642f91cSPeter Korsgaard 
4265b2fc499SJeff Garzik 	/* power up phy */
4275b2fc499SJeff Garzik 	dm_write_reg(dev, DM_GPR_CTRL, 1);
4285b2fc499SJeff Garzik 	dm_write_reg(dev, DM_GPR_DATA, 0);
4295b2fc499SJeff Garzik 
4305b2fc499SJeff Garzik 	/* receive broadcast packets */
4315b2fc499SJeff Garzik 	dm9601_set_multicast(dev->net);
4325b2fc499SJeff Garzik 
4335b2fc499SJeff Garzik 	dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
4345b2fc499SJeff Garzik 	dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
4355b2fc499SJeff Garzik 			  ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
4365b2fc499SJeff Garzik 	mii_nway_restart(&dev->mii);
4375b2fc499SJeff Garzik 
4385b2fc499SJeff Garzik out:
4395b2fc499SJeff Garzik 	return ret;
4405b2fc499SJeff Garzik }
4415b2fc499SJeff Garzik 
dm9601_rx_fixup(struct usbnet * dev,struct sk_buff * skb)4425b2fc499SJeff Garzik static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
4435b2fc499SJeff Garzik {
4445b2fc499SJeff Garzik 	u8 status;
4455b2fc499SJeff Garzik 	int len;
4465b2fc499SJeff Garzik 
4475b2fc499SJeff Garzik 	/* format:
44861189c78SPeter Korsgaard 	   b1: rx status
44961189c78SPeter Korsgaard 	   b2: packet length (incl crc) low
45061189c78SPeter Korsgaard 	   b3: packet length (incl crc) high
45161189c78SPeter Korsgaard 	   b4..n-4: packet data
4525b2fc499SJeff Garzik 	   bn-3..bn: ethernet crc
4535b2fc499SJeff Garzik 	 */
4545b2fc499SJeff Garzik 
4555b2fc499SJeff Garzik 	if (unlikely(skb->len < DM_RX_OVERHEAD)) {
4565b2fc499SJeff Garzik 		dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
4575b2fc499SJeff Garzik 		return 0;
4585b2fc499SJeff Garzik 	}
4595b2fc499SJeff Garzik 
4605b2fc499SJeff Garzik 	status = skb->data[0];
4615b2fc499SJeff Garzik 	len = (skb->data[1] | (skb->data[2] << 8)) - 4;
4625b2fc499SJeff Garzik 
4635b2fc499SJeff Garzik 	if (unlikely(status & 0xbf)) {
4649612101cSHerbert Xu 		if (status & 0x01) dev->net->stats.rx_fifo_errors++;
4659612101cSHerbert Xu 		if (status & 0x02) dev->net->stats.rx_crc_errors++;
4669612101cSHerbert Xu 		if (status & 0x04) dev->net->stats.rx_frame_errors++;
4679612101cSHerbert Xu 		if (status & 0x20) dev->net->stats.rx_missed_errors++;
4689612101cSHerbert Xu 		if (status & 0x90) dev->net->stats.rx_length_errors++;
4695b2fc499SJeff Garzik 		return 0;
4705b2fc499SJeff Garzik 	}
4715b2fc499SJeff Garzik 
4725b2fc499SJeff Garzik 	skb_pull(skb, 3);
4735b2fc499SJeff Garzik 	skb_trim(skb, len);
4745b2fc499SJeff Garzik 
4755b2fc499SJeff Garzik 	return 1;
4765b2fc499SJeff Garzik }
4775b2fc499SJeff Garzik 
dm9601_tx_fixup(struct usbnet * dev,struct sk_buff * skb,gfp_t flags)4785b2fc499SJeff Garzik static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
4795b2fc499SJeff Garzik 				       gfp_t flags)
4805b2fc499SJeff Garzik {
4814263c86dSPeter Korsgaard 	int len, pad;
4825b2fc499SJeff Garzik 
4835b2fc499SJeff Garzik 	/* format:
48461189c78SPeter Korsgaard 	   b1: packet length low
48561189c78SPeter Korsgaard 	   b2: packet length high
4865b2fc499SJeff Garzik 	   b3..n: packet data
4875b2fc499SJeff Garzik 	*/
4885b2fc499SJeff Garzik 
4894263c86dSPeter Korsgaard 	len = skb->len + DM_TX_OVERHEAD;
49023de559bSPeter Korsgaard 
4914263c86dSPeter Korsgaard 	/* workaround for dm962x errata with tx fifo getting out of
4924263c86dSPeter Korsgaard 	 * sync if a USB bulk transfer retry happens right after a
4934263c86dSPeter Korsgaard 	 * packet with odd / maxpacket length by adding up to 3 bytes
4944263c86dSPeter Korsgaard 	 * padding.
4954263c86dSPeter Korsgaard 	 */
4964263c86dSPeter Korsgaard 	while ((len & 1) || !(len % dev->maxpacket))
4974263c86dSPeter Korsgaard 		len++;
4984263c86dSPeter Korsgaard 
4994263c86dSPeter Korsgaard 	len -= DM_TX_OVERHEAD; /* hw header doesn't count as part of length */
5004263c86dSPeter Korsgaard 	pad = len - skb->len;
5014263c86dSPeter Korsgaard 
5024263c86dSPeter Korsgaard 	if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) {
5035b2fc499SJeff Garzik 		struct sk_buff *skb2;
5045b2fc499SJeff Garzik 
5054263c86dSPeter Korsgaard 		skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags);
5065b2fc499SJeff Garzik 		dev_kfree_skb_any(skb);
5075b2fc499SJeff Garzik 		skb = skb2;
5085b2fc499SJeff Garzik 		if (!skb)
5095b2fc499SJeff Garzik 			return NULL;
5105b2fc499SJeff Garzik 	}
5115b2fc499SJeff Garzik 
5125b2fc499SJeff Garzik 	__skb_push(skb, DM_TX_OVERHEAD);
5135b2fc499SJeff Garzik 
5144263c86dSPeter Korsgaard 	if (pad) {
5154263c86dSPeter Korsgaard 		memset(skb->data + skb->len, 0, pad);
5164263c86dSPeter Korsgaard 		__skb_put(skb, pad);
5174263c86dSPeter Korsgaard 	}
5185b2fc499SJeff Garzik 
5195b2fc499SJeff Garzik 	skb->data[0] = len;
5205b2fc499SJeff Garzik 	skb->data[1] = len >> 8;
5215b2fc499SJeff Garzik 
5225b2fc499SJeff Garzik 	return skb;
5235b2fc499SJeff Garzik }
5245b2fc499SJeff Garzik 
dm9601_status(struct usbnet * dev,struct urb * urb)5255b2fc499SJeff Garzik static void dm9601_status(struct usbnet *dev, struct urb *urb)
5265b2fc499SJeff Garzik {
5275b2fc499SJeff Garzik 	int link;
5285b2fc499SJeff Garzik 	u8 *buf;
5295b2fc499SJeff Garzik 
5305b2fc499SJeff Garzik 	/* format:
5315b2fc499SJeff Garzik 	   b0: net status
5325b2fc499SJeff Garzik 	   b1: tx status 1
5335b2fc499SJeff Garzik 	   b2: tx status 2
5345b2fc499SJeff Garzik 	   b3: rx status
5355b2fc499SJeff Garzik 	   b4: rx overflow
5365b2fc499SJeff Garzik 	   b5: rx count
5375b2fc499SJeff Garzik 	   b6: tx count
5385b2fc499SJeff Garzik 	   b7: gpr
5395b2fc499SJeff Garzik 	*/
5405b2fc499SJeff Garzik 
5415b2fc499SJeff Garzik 	if (urb->actual_length < 8)
5425b2fc499SJeff Garzik 		return;
5435b2fc499SJeff Garzik 
5445b2fc499SJeff Garzik 	buf = urb->transfer_buffer;
5455b2fc499SJeff Garzik 
5465b2fc499SJeff Garzik 	link = !!(buf[0] & 0x40);
5475b2fc499SJeff Garzik 	if (netif_carrier_ok(dev->net) != link) {
548c10b1710SMing Lei 		usbnet_link_change(dev, link, 1);
54960b86755SJoe Perches 		netdev_dbg(dev->net, "Link Status is: %d\n", link);
5505b2fc499SJeff Garzik 	}
5515b2fc499SJeff Garzik }
5525b2fc499SJeff Garzik 
dm9601_link_reset(struct usbnet * dev)5535b2fc499SJeff Garzik static int dm9601_link_reset(struct usbnet *dev)
5545b2fc499SJeff Garzik {
5558ae6dacaSDavid Decotigny 	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
5565b2fc499SJeff Garzik 
5575b2fc499SJeff Garzik 	mii_check_media(&dev->mii, 1, 1);
5585b2fc499SJeff Garzik 	mii_ethtool_gset(&dev->mii, &ecmd);
5595b2fc499SJeff Garzik 
5608ae6dacaSDavid Decotigny 	netdev_dbg(dev->net, "link_reset() speed: %u duplex: %d\n",
5618ae6dacaSDavid Decotigny 		   ethtool_cmd_speed(&ecmd), ecmd.duplex);
5625b2fc499SJeff Garzik 
5635b2fc499SJeff Garzik 	return 0;
5645b2fc499SJeff Garzik }
5655b2fc499SJeff Garzik 
5665b2fc499SJeff Garzik static const struct driver_info dm9601_info = {
567cabd0e3aSPeter Korsgaard 	.description	= "Davicom DM96xx USB 10/100 Ethernet",
56837e8273cSBen Hutchings 	.flags		= FLAG_ETHER | FLAG_LINK_INTR,
5695b2fc499SJeff Garzik 	.bind		= dm9601_bind,
5705b2fc499SJeff Garzik 	.rx_fixup	= dm9601_rx_fixup,
5715b2fc499SJeff Garzik 	.tx_fixup	= dm9601_tx_fixup,
5725b2fc499SJeff Garzik 	.status		= dm9601_status,
5735b2fc499SJeff Garzik 	.link_reset	= dm9601_link_reset,
5745b2fc499SJeff Garzik 	.reset		= dm9601_link_reset,
5755b2fc499SJeff Garzik };
5765b2fc499SJeff Garzik 
5775b2fc499SJeff Garzik static const struct usb_device_id products[] = {
5785b2fc499SJeff Garzik 	{
5795b2fc499SJeff Garzik 	 USB_DEVICE(0x07aa, 0x9601),	/* Corega FEther USB-TXC */
5805b2fc499SJeff Garzik 	 .driver_info = (unsigned long)&dm9601_info,
5815b2fc499SJeff Garzik 	 },
5825b2fc499SJeff Garzik 	{
5835b2fc499SJeff Garzik 	 USB_DEVICE(0x0a46, 0x9601),	/* Davicom USB-100 */
5845b2fc499SJeff Garzik 	 .driver_info = (unsigned long)&dm9601_info,
5855b2fc499SJeff Garzik 	 },
5865b2fc499SJeff Garzik 	{
5875b2fc499SJeff Garzik 	 USB_DEVICE(0x0a46, 0x6688),	/* ZT6688 USB NIC */
5885b2fc499SJeff Garzik 	 .driver_info = (unsigned long)&dm9601_info,
5895b2fc499SJeff Garzik 	 },
5905b2fc499SJeff Garzik 	{
5915b2fc499SJeff Garzik 	 USB_DEVICE(0x0a46, 0x0268),	/* ShanTou ST268 USB NIC */
5925b2fc499SJeff Garzik 	 .driver_info = (unsigned long)&dm9601_info,
5935b2fc499SJeff Garzik 	 },
594a06da754SPeter Korsgaard 	{
595a06da754SPeter Korsgaard 	 USB_DEVICE(0x0a46, 0x8515),	/* ADMtek ADM8515 USB NIC */
596a06da754SPeter Korsgaard 	 .driver_info = (unsigned long)&dm9601_info,
597a06da754SPeter Korsgaard 	 },
598b47b4b22SPeter Korsgaard 	{
599b47b4b22SPeter Korsgaard 	USB_DEVICE(0x0a47, 0x9601),	/* Hirose USB-100 */
600b47b4b22SPeter Korsgaard 	.driver_info = (unsigned long)&dm9601_info,
601b47b4b22SPeter Korsgaard 	 },
602a1a69c8dSPeter Korsgaard 	{
603a1a69c8dSPeter Korsgaard 	USB_DEVICE(0x0fe6, 0x8101),	/* DM9601 USB to Fast Ethernet Adapter */
604a1a69c8dSPeter Korsgaard 	.driver_info = (unsigned long)&dm9601_info,
605a1a69c8dSPeter Korsgaard 	 },
60606b71b65SJanusz Krzysztofik 	{
60767158cebSShahar Havivi 	 USB_DEVICE(0x0fe6, 0x9700),	/* DM9601 USB to Fast Ethernet Adapter */
60867158cebSShahar Havivi 	 .driver_info = (unsigned long)&dm9601_info,
60967158cebSShahar Havivi 	 },
61067158cebSShahar Havivi 	{
61106b71b65SJanusz Krzysztofik 	 USB_DEVICE(0x0a46, 0x9000),	/* DM9000E */
61206b71b65SJanusz Krzysztofik 	 .driver_info = (unsigned long)&dm9601_info,
61306b71b65SJanusz Krzysztofik 	 },
6146642f91cSPeter Korsgaard 	{
6156642f91cSPeter Korsgaard 	 USB_DEVICE(0x0a46, 0x9620),	/* DM9620 USB to Fast Ethernet Adapter */
6166642f91cSPeter Korsgaard 	 .driver_info = (unsigned long)&dm9601_info,
6176642f91cSPeter Korsgaard 	 },
6183b387892SPeter Korsgaard 	{
6193b387892SPeter Korsgaard 	 USB_DEVICE(0x0a46, 0x9621),	/* DM9621A USB to Fast Ethernet Adapter */
6203b387892SPeter Korsgaard 	 .driver_info = (unsigned long)&dm9601_info,
6213b387892SPeter Korsgaard 	},
6227c4b5175SPeter Korsgaard 	{
6237c4b5175SPeter Korsgaard 	 USB_DEVICE(0x0a46, 0x9622),	/* DM9622 USB to Fast Ethernet Adapter */
6247c4b5175SPeter Korsgaard 	 .driver_info = (unsigned long)&dm9601_info,
6257c4b5175SPeter Korsgaard 	},
6267c4b5175SPeter Korsgaard 	{
6277c4b5175SPeter Korsgaard 	 USB_DEVICE(0x0a46, 0x0269),	/* DM962OA USB to Fast Ethernet Adapter */
6287c4b5175SPeter Korsgaard 	 .driver_info = (unsigned long)&dm9601_info,
6297c4b5175SPeter Korsgaard 	},
6307c4b5175SPeter Korsgaard 	{
6317c4b5175SPeter Korsgaard 	 USB_DEVICE(0x0a46, 0x1269),	/* DM9621A USB to Fast Ethernet Adapter */
6327c4b5175SPeter Korsgaard 	 .driver_info = (unsigned long)&dm9601_info,
6337c4b5175SPeter Korsgaard 	},
634a609d025SKamil Lorenc 	{
635a609d025SKamil Lorenc 	 USB_DEVICE(0x0586, 0x3427),	/* ZyXEL Keenetic Plus DSL xDSL modem */
636a609d025SKamil Lorenc 	 .driver_info = (unsigned long)&dm9601_info,
637a609d025SKamil Lorenc 	},
6385b2fc499SJeff Garzik 	{},			// END
6395b2fc499SJeff Garzik };
6405b2fc499SJeff Garzik 
6415b2fc499SJeff Garzik MODULE_DEVICE_TABLE(usb, products);
6425b2fc499SJeff Garzik 
6435b2fc499SJeff Garzik static struct usb_driver dm9601_driver = {
6445b2fc499SJeff Garzik 	.name = "dm9601",
6455b2fc499SJeff Garzik 	.id_table = products,
6465b2fc499SJeff Garzik 	.probe = usbnet_probe,
6475b2fc499SJeff Garzik 	.disconnect = usbnet_disconnect,
6485b2fc499SJeff Garzik 	.suspend = usbnet_suspend,
6495b2fc499SJeff Garzik 	.resume = usbnet_resume,
650e1f12eb6SSarah Sharp 	.disable_hub_initiated_lpm = 1,
6515b2fc499SJeff Garzik };
6525b2fc499SJeff Garzik 
653d632eb1bSGreg Kroah-Hartman module_usb_driver(dm9601_driver);
6545b2fc499SJeff Garzik 
6555b2fc499SJeff Garzik MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
656cabd0e3aSPeter Korsgaard MODULE_DESCRIPTION("Davicom DM96xx USB 10/100 ethernet devices");
6575b2fc499SJeff Garzik MODULE_LICENSE("GPL");
658