1b4cdae20SChristian Riesch /* 2b4cdae20SChristian Riesch * ASIX AX8817X based USB 2.0 Ethernet Devices 3b4cdae20SChristian Riesch * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com> 4b4cdae20SChristian Riesch * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> 5b4cdae20SChristian Riesch * Copyright (C) 2006 James Painter <jamie.painter@iname.com> 6b4cdae20SChristian Riesch * Copyright (c) 2002-2003 TiVo Inc. 7b4cdae20SChristian Riesch * 8b4cdae20SChristian Riesch * This program is free software; you can redistribute it and/or modify 9b4cdae20SChristian Riesch * it under the terms of the GNU General Public License as published by 10b4cdae20SChristian Riesch * the Free Software Foundation; either version 2 of the License, or 11b4cdae20SChristian Riesch * (at your option) any later version. 12b4cdae20SChristian Riesch * 13b4cdae20SChristian Riesch * This program is distributed in the hope that it will be useful, 14b4cdae20SChristian Riesch * but WITHOUT ANY WARRANTY; without even the implied warranty of 15b4cdae20SChristian Riesch * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16b4cdae20SChristian Riesch * GNU General Public License for more details. 17b4cdae20SChristian Riesch * 18b4cdae20SChristian Riesch * You should have received a copy of the GNU General Public License 19b4cdae20SChristian Riesch * along with this program; if not, write to the Free Software 20b4cdae20SChristian Riesch * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21b4cdae20SChristian Riesch */ 22b4cdae20SChristian Riesch 23607740bcSChristian Riesch #include "asix.h" 24b4cdae20SChristian Riesch 25607740bcSChristian Riesch int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 26b4cdae20SChristian Riesch u16 size, void *data) 27b4cdae20SChristian Riesch { 28b4cdae20SChristian Riesch void *buf; 29b4cdae20SChristian Riesch int err = -ENOMEM; 30b4cdae20SChristian Riesch 31b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", 32b4cdae20SChristian Riesch cmd, value, index, size); 33b4cdae20SChristian Riesch 34b4cdae20SChristian Riesch buf = kmalloc(size, GFP_KERNEL); 35b4cdae20SChristian Riesch if (!buf) 36b4cdae20SChristian Riesch goto out; 37b4cdae20SChristian Riesch 38b4cdae20SChristian Riesch err = usb_control_msg( 39b4cdae20SChristian Riesch dev->udev, 40b4cdae20SChristian Riesch usb_rcvctrlpipe(dev->udev, 0), 41b4cdae20SChristian Riesch cmd, 42b4cdae20SChristian Riesch USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 43b4cdae20SChristian Riesch value, 44b4cdae20SChristian Riesch index, 45b4cdae20SChristian Riesch buf, 46b4cdae20SChristian Riesch size, 47b4cdae20SChristian Riesch USB_CTRL_GET_TIMEOUT); 48b4cdae20SChristian Riesch if (err == size) 49b4cdae20SChristian Riesch memcpy(data, buf, size); 50b4cdae20SChristian Riesch else if (err >= 0) 51b4cdae20SChristian Riesch err = -EINVAL; 52b4cdae20SChristian Riesch kfree(buf); 53b4cdae20SChristian Riesch 54b4cdae20SChristian Riesch out: 55b4cdae20SChristian Riesch return err; 56b4cdae20SChristian Riesch } 57b4cdae20SChristian Riesch 58607740bcSChristian Riesch int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 59b4cdae20SChristian Riesch u16 size, void *data) 60b4cdae20SChristian Riesch { 61b4cdae20SChristian Riesch void *buf = NULL; 62b4cdae20SChristian Riesch int err = -ENOMEM; 63b4cdae20SChristian Riesch 64b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", 65b4cdae20SChristian Riesch cmd, value, index, size); 66b4cdae20SChristian Riesch 67b4cdae20SChristian Riesch if (data) { 68b4cdae20SChristian Riesch buf = kmemdup(data, size, GFP_KERNEL); 69b4cdae20SChristian Riesch if (!buf) 70b4cdae20SChristian Riesch goto out; 71b4cdae20SChristian Riesch } 72b4cdae20SChristian Riesch 73b4cdae20SChristian Riesch err = usb_control_msg( 74b4cdae20SChristian Riesch dev->udev, 75b4cdae20SChristian Riesch usb_sndctrlpipe(dev->udev, 0), 76b4cdae20SChristian Riesch cmd, 77b4cdae20SChristian Riesch USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 78b4cdae20SChristian Riesch value, 79b4cdae20SChristian Riesch index, 80b4cdae20SChristian Riesch buf, 81b4cdae20SChristian Riesch size, 82b4cdae20SChristian Riesch USB_CTRL_SET_TIMEOUT); 83b4cdae20SChristian Riesch kfree(buf); 84b4cdae20SChristian Riesch 85b4cdae20SChristian Riesch out: 86b4cdae20SChristian Riesch return err; 87b4cdae20SChristian Riesch } 88b4cdae20SChristian Riesch 89b4cdae20SChristian Riesch static void asix_async_cmd_callback(struct urb *urb) 90b4cdae20SChristian Riesch { 91b4cdae20SChristian Riesch struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; 92b4cdae20SChristian Riesch int status = urb->status; 93b4cdae20SChristian Riesch 94b4cdae20SChristian Riesch if (status < 0) 95b4cdae20SChristian Riesch printk(KERN_DEBUG "asix_async_cmd_callback() failed with %d", 96b4cdae20SChristian Riesch status); 97b4cdae20SChristian Riesch 98b4cdae20SChristian Riesch kfree(req); 99b4cdae20SChristian Riesch usb_free_urb(urb); 100b4cdae20SChristian Riesch } 101b4cdae20SChristian Riesch 102607740bcSChristian Riesch void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, 103b4cdae20SChristian Riesch u16 size, void *data) 104b4cdae20SChristian Riesch { 105b4cdae20SChristian Riesch struct usb_ctrlrequest *req; 106b4cdae20SChristian Riesch int status; 107b4cdae20SChristian Riesch struct urb *urb; 108b4cdae20SChristian Riesch 109b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_cmd_async() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", 110b4cdae20SChristian Riesch cmd, value, index, size); 111b4cdae20SChristian Riesch 112b4cdae20SChristian Riesch urb = usb_alloc_urb(0, GFP_ATOMIC); 113b4cdae20SChristian Riesch if (!urb) { 114b4cdae20SChristian Riesch netdev_err(dev->net, "Error allocating URB in write_cmd_async!\n"); 115b4cdae20SChristian Riesch return; 116b4cdae20SChristian Riesch } 117b4cdae20SChristian Riesch 118b4cdae20SChristian Riesch req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); 119b4cdae20SChristian Riesch if (!req) { 120b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to allocate memory for control request\n"); 121b4cdae20SChristian Riesch usb_free_urb(urb); 122b4cdae20SChristian Riesch return; 123b4cdae20SChristian Riesch } 124b4cdae20SChristian Riesch 125b4cdae20SChristian Riesch req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 126b4cdae20SChristian Riesch req->bRequest = cmd; 127b4cdae20SChristian Riesch req->wValue = cpu_to_le16(value); 128b4cdae20SChristian Riesch req->wIndex = cpu_to_le16(index); 129b4cdae20SChristian Riesch req->wLength = cpu_to_le16(size); 130b4cdae20SChristian Riesch 131b4cdae20SChristian Riesch usb_fill_control_urb(urb, dev->udev, 132b4cdae20SChristian Riesch usb_sndctrlpipe(dev->udev, 0), 133b4cdae20SChristian Riesch (void *)req, data, size, 134b4cdae20SChristian Riesch asix_async_cmd_callback, req); 135b4cdae20SChristian Riesch 136b4cdae20SChristian Riesch status = usb_submit_urb(urb, GFP_ATOMIC); 137b4cdae20SChristian Riesch if (status < 0) { 138b4cdae20SChristian Riesch netdev_err(dev->net, "Error submitting the control message: status=%d\n", 139b4cdae20SChristian Riesch status); 140b4cdae20SChristian Riesch kfree(req); 141b4cdae20SChristian Riesch usb_free_urb(urb); 142b4cdae20SChristian Riesch } 143b4cdae20SChristian Riesch } 144b4cdae20SChristian Riesch 145607740bcSChristian Riesch int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 146b4cdae20SChristian Riesch { 147b4cdae20SChristian Riesch int offset = 0; 148b4cdae20SChristian Riesch 149b4cdae20SChristian Riesch while (offset + sizeof(u32) < skb->len) { 150b4cdae20SChristian Riesch struct sk_buff *ax_skb; 151b4cdae20SChristian Riesch u16 size; 152b4cdae20SChristian Riesch u32 header = get_unaligned_le32(skb->data + offset); 153b4cdae20SChristian Riesch 154b4cdae20SChristian Riesch offset += sizeof(u32); 155b4cdae20SChristian Riesch 156b4cdae20SChristian Riesch /* get the packet length */ 157b4cdae20SChristian Riesch size = (u16) (header & 0x7ff); 158b4cdae20SChristian Riesch if (size != ((~header >> 16) & 0x07ff)) { 159b4cdae20SChristian Riesch netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); 160b4cdae20SChristian Riesch return 0; 161b4cdae20SChristian Riesch } 162b4cdae20SChristian Riesch 163b4cdae20SChristian Riesch if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) || 164b4cdae20SChristian Riesch (size + offset > skb->len)) { 165b4cdae20SChristian Riesch netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", 166b4cdae20SChristian Riesch size); 167b4cdae20SChristian Riesch return 0; 168b4cdae20SChristian Riesch } 169b4cdae20SChristian Riesch ax_skb = netdev_alloc_skb_ip_align(dev->net, size); 170b4cdae20SChristian Riesch if (!ax_skb) 171b4cdae20SChristian Riesch return 0; 172b4cdae20SChristian Riesch 173b4cdae20SChristian Riesch skb_put(ax_skb, size); 174b4cdae20SChristian Riesch memcpy(ax_skb->data, skb->data + offset, size); 175b4cdae20SChristian Riesch usbnet_skb_return(dev, ax_skb); 176b4cdae20SChristian Riesch 177b4cdae20SChristian Riesch offset += (size + 1) & 0xfffe; 178b4cdae20SChristian Riesch } 179b4cdae20SChristian Riesch 180b4cdae20SChristian Riesch if (skb->len != offset) { 181b4cdae20SChristian Riesch netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n", 182b4cdae20SChristian Riesch skb->len); 183b4cdae20SChristian Riesch return 0; 184b4cdae20SChristian Riesch } 185b4cdae20SChristian Riesch return 1; 186b4cdae20SChristian Riesch } 187b4cdae20SChristian Riesch 188607740bcSChristian Riesch struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 189b4cdae20SChristian Riesch gfp_t flags) 190b4cdae20SChristian Riesch { 191b4cdae20SChristian Riesch int padlen; 192b4cdae20SChristian Riesch int headroom = skb_headroom(skb); 193b4cdae20SChristian Riesch int tailroom = skb_tailroom(skb); 194b4cdae20SChristian Riesch u32 packet_len; 195b4cdae20SChristian Riesch u32 padbytes = 0xffff0000; 196b4cdae20SChristian Riesch 197b4cdae20SChristian Riesch padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; 198b4cdae20SChristian Riesch 199b4cdae20SChristian Riesch /* We need to push 4 bytes in front of frame (packet_len) 200b4cdae20SChristian Riesch * and maybe add 4 bytes after the end (if padlen is 4) 201b4cdae20SChristian Riesch * 202b4cdae20SChristian Riesch * Avoid skb_copy_expand() expensive call, using following rules : 203b4cdae20SChristian Riesch * - We are allowed to push 4 bytes in headroom if skb_header_cloned() 204b4cdae20SChristian Riesch * is false (and if we have 4 bytes of headroom) 205b4cdae20SChristian Riesch * - We are allowed to put 4 bytes at tail if skb_cloned() 206b4cdae20SChristian Riesch * is false (and if we have 4 bytes of tailroom) 207b4cdae20SChristian Riesch * 208b4cdae20SChristian Riesch * TCP packets for example are cloned, but skb_header_release() 209b4cdae20SChristian Riesch * was called in tcp stack, allowing us to use headroom for our needs. 210b4cdae20SChristian Riesch */ 211b4cdae20SChristian Riesch if (!skb_header_cloned(skb) && 212b4cdae20SChristian Riesch !(padlen && skb_cloned(skb)) && 213b4cdae20SChristian Riesch headroom + tailroom >= 4 + padlen) { 214b4cdae20SChristian Riesch /* following should not happen, but better be safe */ 215b4cdae20SChristian Riesch if (headroom < 4 || 216b4cdae20SChristian Riesch tailroom < padlen) { 217b4cdae20SChristian Riesch skb->data = memmove(skb->head + 4, skb->data, skb->len); 218b4cdae20SChristian Riesch skb_set_tail_pointer(skb, skb->len); 219b4cdae20SChristian Riesch } 220b4cdae20SChristian Riesch } else { 221b4cdae20SChristian Riesch struct sk_buff *skb2; 222b4cdae20SChristian Riesch 223b4cdae20SChristian Riesch skb2 = skb_copy_expand(skb, 4, padlen, flags); 224b4cdae20SChristian Riesch dev_kfree_skb_any(skb); 225b4cdae20SChristian Riesch skb = skb2; 226b4cdae20SChristian Riesch if (!skb) 227b4cdae20SChristian Riesch return NULL; 228b4cdae20SChristian Riesch } 229b4cdae20SChristian Riesch 230b4cdae20SChristian Riesch packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; 231b4cdae20SChristian Riesch skb_push(skb, 4); 232b4cdae20SChristian Riesch cpu_to_le32s(&packet_len); 233b4cdae20SChristian Riesch skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); 234b4cdae20SChristian Riesch 235b4cdae20SChristian Riesch if (padlen) { 236b4cdae20SChristian Riesch cpu_to_le32s(&padbytes); 237b4cdae20SChristian Riesch memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); 238b4cdae20SChristian Riesch skb_put(skb, sizeof(padbytes)); 239b4cdae20SChristian Riesch } 240b4cdae20SChristian Riesch return skb; 241b4cdae20SChristian Riesch } 242b4cdae20SChristian Riesch 243607740bcSChristian Riesch int asix_set_sw_mii(struct usbnet *dev) 244b4cdae20SChristian Riesch { 245b4cdae20SChristian Riesch int ret; 246b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); 247b4cdae20SChristian Riesch if (ret < 0) 248b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to enable software MII access\n"); 249b4cdae20SChristian Riesch return ret; 250b4cdae20SChristian Riesch } 251b4cdae20SChristian Riesch 252607740bcSChristian Riesch int asix_set_hw_mii(struct usbnet *dev) 253b4cdae20SChristian Riesch { 254b4cdae20SChristian Riesch int ret; 255b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); 256b4cdae20SChristian Riesch if (ret < 0) 257b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to enable hardware MII access\n"); 258b4cdae20SChristian Riesch return ret; 259b4cdae20SChristian Riesch } 260b4cdae20SChristian Riesch 26116626b0cSChristian Riesch int asix_read_phy_addr(struct usbnet *dev, int internal) 262b4cdae20SChristian Riesch { 26316626b0cSChristian Riesch int offset = (internal ? 1 : 0); 264b4cdae20SChristian Riesch u8 buf[2]; 265b4cdae20SChristian Riesch int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); 266b4cdae20SChristian Riesch 267b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_get_phy_addr()\n"); 268b4cdae20SChristian Riesch 269b4cdae20SChristian Riesch if (ret < 0) { 270b4cdae20SChristian Riesch netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret); 271b4cdae20SChristian Riesch goto out; 272b4cdae20SChristian Riesch } 273b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n", 274b4cdae20SChristian Riesch *((__le16 *)buf)); 27516626b0cSChristian Riesch ret = buf[offset]; 276b4cdae20SChristian Riesch 277b4cdae20SChristian Riesch out: 278b4cdae20SChristian Riesch return ret; 279b4cdae20SChristian Riesch } 280b4cdae20SChristian Riesch 28116626b0cSChristian Riesch int asix_get_phy_addr(struct usbnet *dev) 28216626b0cSChristian Riesch { 28316626b0cSChristian Riesch /* return the address of the internal phy */ 28416626b0cSChristian Riesch return asix_read_phy_addr(dev, 1); 28516626b0cSChristian Riesch } 28616626b0cSChristian Riesch 28716626b0cSChristian Riesch 288607740bcSChristian Riesch int asix_sw_reset(struct usbnet *dev, u8 flags) 289b4cdae20SChristian Riesch { 290b4cdae20SChristian Riesch int ret; 291b4cdae20SChristian Riesch 292b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); 293b4cdae20SChristian Riesch if (ret < 0) 294b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); 295b4cdae20SChristian Riesch 296b4cdae20SChristian Riesch return ret; 297b4cdae20SChristian Riesch } 298b4cdae20SChristian Riesch 299607740bcSChristian Riesch u16 asix_read_rx_ctl(struct usbnet *dev) 300b4cdae20SChristian Riesch { 301b4cdae20SChristian Riesch __le16 v; 302b4cdae20SChristian Riesch int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v); 303b4cdae20SChristian Riesch 304b4cdae20SChristian Riesch if (ret < 0) { 305b4cdae20SChristian Riesch netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); 306b4cdae20SChristian Riesch goto out; 307b4cdae20SChristian Riesch } 308b4cdae20SChristian Riesch ret = le16_to_cpu(v); 309b4cdae20SChristian Riesch out: 310b4cdae20SChristian Riesch return ret; 311b4cdae20SChristian Riesch } 312b4cdae20SChristian Riesch 313607740bcSChristian Riesch int asix_write_rx_ctl(struct usbnet *dev, u16 mode) 314b4cdae20SChristian Riesch { 315b4cdae20SChristian Riesch int ret; 316b4cdae20SChristian Riesch 317b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); 318b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); 319b4cdae20SChristian Riesch if (ret < 0) 320b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", 321b4cdae20SChristian Riesch mode, ret); 322b4cdae20SChristian Riesch 323b4cdae20SChristian Riesch return ret; 324b4cdae20SChristian Riesch } 325b4cdae20SChristian Riesch 326607740bcSChristian Riesch u16 asix_read_medium_status(struct usbnet *dev) 327b4cdae20SChristian Riesch { 328b4cdae20SChristian Riesch __le16 v; 329b4cdae20SChristian Riesch int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); 330b4cdae20SChristian Riesch 331b4cdae20SChristian Riesch if (ret < 0) { 332b4cdae20SChristian Riesch netdev_err(dev->net, "Error reading Medium Status register: %02x\n", 333b4cdae20SChristian Riesch ret); 334b4cdae20SChristian Riesch return ret; /* TODO: callers not checking for error ret */ 335b4cdae20SChristian Riesch } 336b4cdae20SChristian Riesch 337b4cdae20SChristian Riesch return le16_to_cpu(v); 338b4cdae20SChristian Riesch 339b4cdae20SChristian Riesch } 340b4cdae20SChristian Riesch 341607740bcSChristian Riesch int asix_write_medium_mode(struct usbnet *dev, u16 mode) 342b4cdae20SChristian Riesch { 343b4cdae20SChristian Riesch int ret; 344b4cdae20SChristian Riesch 345b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); 346b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); 347b4cdae20SChristian Riesch if (ret < 0) 348b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", 349b4cdae20SChristian Riesch mode, ret); 350b4cdae20SChristian Riesch 351b4cdae20SChristian Riesch return ret; 352b4cdae20SChristian Riesch } 353b4cdae20SChristian Riesch 354607740bcSChristian Riesch int asix_write_gpio(struct usbnet *dev, u16 value, int sleep) 355b4cdae20SChristian Riesch { 356b4cdae20SChristian Riesch int ret; 357b4cdae20SChristian Riesch 358b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); 359b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); 360b4cdae20SChristian Riesch if (ret < 0) 361b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", 362b4cdae20SChristian Riesch value, ret); 363b4cdae20SChristian Riesch 364b4cdae20SChristian Riesch if (sleep) 365b4cdae20SChristian Riesch msleep(sleep); 366b4cdae20SChristian Riesch 367b4cdae20SChristian Riesch return ret; 368b4cdae20SChristian Riesch } 369b4cdae20SChristian Riesch 370b4cdae20SChristian Riesch /* 371b4cdae20SChristian Riesch * AX88772 & AX88178 have a 16-bit RX_CTL value 372b4cdae20SChristian Riesch */ 373607740bcSChristian Riesch void asix_set_multicast(struct net_device *net) 374b4cdae20SChristian Riesch { 375b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 376b4cdae20SChristian Riesch struct asix_data *data = (struct asix_data *)&dev->data; 377b4cdae20SChristian Riesch u16 rx_ctl = AX_DEFAULT_RX_CTL; 378b4cdae20SChristian Riesch 379b4cdae20SChristian Riesch if (net->flags & IFF_PROMISC) { 380b4cdae20SChristian Riesch rx_ctl |= AX_RX_CTL_PRO; 381b4cdae20SChristian Riesch } else if (net->flags & IFF_ALLMULTI || 382b4cdae20SChristian Riesch netdev_mc_count(net) > AX_MAX_MCAST) { 383b4cdae20SChristian Riesch rx_ctl |= AX_RX_CTL_AMALL; 384b4cdae20SChristian Riesch } else if (netdev_mc_empty(net)) { 385b4cdae20SChristian Riesch /* just broadcast and directed */ 386b4cdae20SChristian Riesch } else { 387b4cdae20SChristian Riesch /* We use the 20 byte dev->data 388b4cdae20SChristian Riesch * for our 8 byte filter buffer 389b4cdae20SChristian Riesch * to avoid allocating memory that 390b4cdae20SChristian Riesch * is tricky to free later */ 391b4cdae20SChristian Riesch struct netdev_hw_addr *ha; 392b4cdae20SChristian Riesch u32 crc_bits; 393b4cdae20SChristian Riesch 394b4cdae20SChristian Riesch memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); 395b4cdae20SChristian Riesch 396b4cdae20SChristian Riesch /* Build the multicast hash filter. */ 397b4cdae20SChristian Riesch netdev_for_each_mc_addr(ha, net) { 398b4cdae20SChristian Riesch crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; 399b4cdae20SChristian Riesch data->multi_filter[crc_bits >> 3] |= 400b4cdae20SChristian Riesch 1 << (crc_bits & 7); 401b4cdae20SChristian Riesch } 402b4cdae20SChristian Riesch 403b4cdae20SChristian Riesch asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, 404b4cdae20SChristian Riesch AX_MCAST_FILTER_SIZE, data->multi_filter); 405b4cdae20SChristian Riesch 406b4cdae20SChristian Riesch rx_ctl |= AX_RX_CTL_AM; 407b4cdae20SChristian Riesch } 408b4cdae20SChristian Riesch 409b4cdae20SChristian Riesch asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); 410b4cdae20SChristian Riesch } 411b4cdae20SChristian Riesch 412607740bcSChristian Riesch int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) 413b4cdae20SChristian Riesch { 414b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(netdev); 415b4cdae20SChristian Riesch __le16 res; 416b4cdae20SChristian Riesch 417b4cdae20SChristian Riesch mutex_lock(&dev->phy_mutex); 418b4cdae20SChristian Riesch asix_set_sw_mii(dev); 419b4cdae20SChristian Riesch asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, 420b4cdae20SChristian Riesch (__u16)loc, 2, &res); 421b4cdae20SChristian Riesch asix_set_hw_mii(dev); 422b4cdae20SChristian Riesch mutex_unlock(&dev->phy_mutex); 423b4cdae20SChristian Riesch 424b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 425b4cdae20SChristian Riesch phy_id, loc, le16_to_cpu(res)); 426b4cdae20SChristian Riesch 427b4cdae20SChristian Riesch return le16_to_cpu(res); 428b4cdae20SChristian Riesch } 429b4cdae20SChristian Riesch 430607740bcSChristian Riesch void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) 431b4cdae20SChristian Riesch { 432b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(netdev); 433b4cdae20SChristian Riesch __le16 res = cpu_to_le16(val); 434b4cdae20SChristian Riesch 435b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 436b4cdae20SChristian Riesch phy_id, loc, val); 437b4cdae20SChristian Riesch mutex_lock(&dev->phy_mutex); 438b4cdae20SChristian Riesch asix_set_sw_mii(dev); 439b4cdae20SChristian Riesch asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); 440b4cdae20SChristian Riesch asix_set_hw_mii(dev); 441b4cdae20SChristian Riesch mutex_unlock(&dev->phy_mutex); 442b4cdae20SChristian Riesch } 443b4cdae20SChristian Riesch 444607740bcSChristian Riesch void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 445b4cdae20SChristian Riesch { 446b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 447b4cdae20SChristian Riesch u8 opt; 448b4cdae20SChristian Riesch 449b4cdae20SChristian Riesch if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { 450b4cdae20SChristian Riesch wolinfo->supported = 0; 451b4cdae20SChristian Riesch wolinfo->wolopts = 0; 452b4cdae20SChristian Riesch return; 453b4cdae20SChristian Riesch } 454b4cdae20SChristian Riesch wolinfo->supported = WAKE_PHY | WAKE_MAGIC; 455b4cdae20SChristian Riesch wolinfo->wolopts = 0; 456b4cdae20SChristian Riesch if (opt & AX_MONITOR_LINK) 457b4cdae20SChristian Riesch wolinfo->wolopts |= WAKE_PHY; 458b4cdae20SChristian Riesch if (opt & AX_MONITOR_MAGIC) 459b4cdae20SChristian Riesch wolinfo->wolopts |= WAKE_MAGIC; 460b4cdae20SChristian Riesch } 461b4cdae20SChristian Riesch 462607740bcSChristian Riesch int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 463b4cdae20SChristian Riesch { 464b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 465b4cdae20SChristian Riesch u8 opt = 0; 466b4cdae20SChristian Riesch 467b4cdae20SChristian Riesch if (wolinfo->wolopts & WAKE_PHY) 468b4cdae20SChristian Riesch opt |= AX_MONITOR_LINK; 469b4cdae20SChristian Riesch if (wolinfo->wolopts & WAKE_MAGIC) 470b4cdae20SChristian Riesch opt |= AX_MONITOR_MAGIC; 471b4cdae20SChristian Riesch 472b4cdae20SChristian Riesch if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, 473b4cdae20SChristian Riesch opt, 0, 0, NULL) < 0) 474b4cdae20SChristian Riesch return -EINVAL; 475b4cdae20SChristian Riesch 476b4cdae20SChristian Riesch return 0; 477b4cdae20SChristian Riesch } 478b4cdae20SChristian Riesch 479607740bcSChristian Riesch int asix_get_eeprom_len(struct net_device *net) 480b4cdae20SChristian Riesch { 481*ceb02c91SChristian Riesch return AX_EEPROM_LEN; 482b4cdae20SChristian Riesch } 483b4cdae20SChristian Riesch 484607740bcSChristian Riesch int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 485607740bcSChristian Riesch u8 *data) 486b4cdae20SChristian Riesch { 487b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 488*ceb02c91SChristian Riesch u16 *eeprom_buff; 489*ceb02c91SChristian Riesch int first_word, last_word; 490b4cdae20SChristian Riesch int i; 491b4cdae20SChristian Riesch 492*ceb02c91SChristian Riesch if (eeprom->len == 0) 493b4cdae20SChristian Riesch return -EINVAL; 494b4cdae20SChristian Riesch 495b4cdae20SChristian Riesch eeprom->magic = AX_EEPROM_MAGIC; 496b4cdae20SChristian Riesch 497*ceb02c91SChristian Riesch first_word = eeprom->offset >> 1; 498*ceb02c91SChristian Riesch last_word = (eeprom->offset + eeprom->len - 1) >> 1; 499*ceb02c91SChristian Riesch 500*ceb02c91SChristian Riesch eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), 501*ceb02c91SChristian Riesch GFP_KERNEL); 502*ceb02c91SChristian Riesch if (!eeprom_buff) 503*ceb02c91SChristian Riesch return -ENOMEM; 504*ceb02c91SChristian Riesch 505b4cdae20SChristian Riesch /* ax8817x returns 2 bytes from eeprom on read */ 506*ceb02c91SChristian Riesch for (i = first_word; i <= last_word; i++) { 507*ceb02c91SChristian Riesch if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, 508*ceb02c91SChristian Riesch &(eeprom_buff[i - first_word])) < 0) { 509*ceb02c91SChristian Riesch kfree(eeprom_buff); 510*ceb02c91SChristian Riesch return -EIO; 511b4cdae20SChristian Riesch } 512*ceb02c91SChristian Riesch } 513*ceb02c91SChristian Riesch 514*ceb02c91SChristian Riesch memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); 515*ceb02c91SChristian Riesch kfree(eeprom_buff); 516b4cdae20SChristian Riesch return 0; 517b4cdae20SChristian Riesch } 518b4cdae20SChristian Riesch 519607740bcSChristian Riesch void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) 520b4cdae20SChristian Riesch { 521b4cdae20SChristian Riesch /* Inherit standard device info */ 522b4cdae20SChristian Riesch usbnet_get_drvinfo(net, info); 523b4cdae20SChristian Riesch strncpy (info->driver, DRIVER_NAME, sizeof info->driver); 524b4cdae20SChristian Riesch strncpy (info->version, DRIVER_VERSION, sizeof info->version); 525*ceb02c91SChristian Riesch info->eedump_len = AX_EEPROM_LEN; 526b4cdae20SChristian Riesch } 527b4cdae20SChristian Riesch 528607740bcSChristian Riesch int asix_set_mac_address(struct net_device *net, void *p) 529b4cdae20SChristian Riesch { 530b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 531b4cdae20SChristian Riesch struct asix_data *data = (struct asix_data *)&dev->data; 532b4cdae20SChristian Riesch struct sockaddr *addr = p; 533b4cdae20SChristian Riesch 534b4cdae20SChristian Riesch if (netif_running(net)) 535b4cdae20SChristian Riesch return -EBUSY; 536b4cdae20SChristian Riesch if (!is_valid_ether_addr(addr->sa_data)) 537b4cdae20SChristian Riesch return -EADDRNOTAVAIL; 538b4cdae20SChristian Riesch 539b4cdae20SChristian Riesch memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); 540b4cdae20SChristian Riesch 541b4cdae20SChristian Riesch /* We use the 20 byte dev->data 542b4cdae20SChristian Riesch * for our 6 byte mac buffer 543b4cdae20SChristian Riesch * to avoid allocating memory that 544b4cdae20SChristian Riesch * is tricky to free later */ 545b4cdae20SChristian Riesch memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); 546b4cdae20SChristian Riesch asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, 547b4cdae20SChristian Riesch data->mac_addr); 548b4cdae20SChristian Riesch 549b4cdae20SChristian Riesch return 0; 550b4cdae20SChristian Riesch } 551