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 199cb00073SJeff Kirsher * along with this program; if not, see <http://www.gnu.org/licenses/>. 20b4cdae20SChristian Riesch */ 21b4cdae20SChristian Riesch 22607740bcSChristian Riesch #include "asix.h" 23b4cdae20SChristian Riesch 24607740bcSChristian Riesch int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 25b4cdae20SChristian Riesch u16 size, void *data) 26b4cdae20SChristian Riesch { 270bc69efbSMing Lei int ret; 280bc69efbSMing Lei ret = usbnet_read_cmd(dev, cmd, 29b4cdae20SChristian Riesch USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 300bc69efbSMing Lei value, index, data, size); 31b4cdae20SChristian Riesch 320bc69efbSMing Lei if (ret != size && ret >= 0) 330bc69efbSMing Lei return -EINVAL; 340bc69efbSMing Lei return ret; 35b4cdae20SChristian Riesch } 36b4cdae20SChristian Riesch 37607740bcSChristian Riesch int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, 38b4cdae20SChristian Riesch u16 size, void *data) 39b4cdae20SChristian Riesch { 400bc69efbSMing Lei return usbnet_write_cmd(dev, cmd, 41b4cdae20SChristian Riesch USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 420bc69efbSMing Lei value, index, data, size); 43b4cdae20SChristian Riesch } 44b4cdae20SChristian Riesch 45607740bcSChristian Riesch void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, 46b4cdae20SChristian Riesch u16 size, void *data) 47b4cdae20SChristian Riesch { 480bc69efbSMing Lei usbnet_write_cmd_async(dev, cmd, 490bc69efbSMing Lei USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 500bc69efbSMing Lei value, index, data, size); 51b4cdae20SChristian Riesch } 52b4cdae20SChristian Riesch 538b5b6f54SLucas Stach int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, 548b5b6f54SLucas Stach struct asix_rx_fixup_info *rx) 55b4cdae20SChristian Riesch { 56b4cdae20SChristian Riesch int offset = 0; 577b0378f5SDean Jenkins u16 size; 58b4cdae20SChristian Riesch 593f30b158SDean Jenkins /* When an Ethernet frame spans multiple URB socket buffers, 603f30b158SDean Jenkins * do a sanity test for the Data header synchronisation. 613f30b158SDean Jenkins * Attempt to detect the situation of the previous socket buffer having 623f30b158SDean Jenkins * been truncated or a socket buffer was missing. These situations 633f30b158SDean Jenkins * cause a discontinuity in the data stream and therefore need to avoid 643f30b158SDean Jenkins * appending bad data to the end of the current netdev socket buffer. 653f30b158SDean Jenkins * Also avoid unnecessarily discarding a good current netdev socket 663f30b158SDean Jenkins * buffer. 673f30b158SDean Jenkins */ 683f30b158SDean Jenkins if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { 693f30b158SDean Jenkins offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); 703f30b158SDean Jenkins rx->header = get_unaligned_le32(skb->data + offset); 713f30b158SDean Jenkins offset = 0; 723f30b158SDean Jenkins 733f30b158SDean Jenkins size = (u16)(rx->header & 0x7ff); 743f30b158SDean Jenkins if (size != ((~rx->header >> 16) & 0x7ff)) { 753f30b158SDean Jenkins netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", 763f30b158SDean Jenkins rx->remaining); 776a570814SDean Jenkins if (rx->ax_skb) { 783f30b158SDean Jenkins kfree_skb(rx->ax_skb); 793f30b158SDean Jenkins rx->ax_skb = NULL; 806a570814SDean Jenkins /* Discard the incomplete netdev Ethernet frame 816a570814SDean Jenkins * and assume the Data header is at the start of 826a570814SDean Jenkins * the current URB socket buffer. 833f30b158SDean Jenkins */ 846a570814SDean Jenkins } 853f30b158SDean Jenkins rx->remaining = 0; 863f30b158SDean Jenkins } 873f30b158SDean Jenkins } 883f30b158SDean Jenkins 898b5b6f54SLucas Stach while (offset + sizeof(u16) <= skb->len) { 907b0378f5SDean Jenkins u16 copy_length; 918b5b6f54SLucas Stach unsigned char *data; 92b4cdae20SChristian Riesch 937b0378f5SDean Jenkins if (!rx->remaining) { 943bfc69abSDean Jenkins if (skb->len - offset == sizeof(u16)) { 958b5b6f54SLucas Stach rx->header = get_unaligned_le16( 968b5b6f54SLucas Stach skb->data + offset); 978b5b6f54SLucas Stach rx->split_head = true; 988b5b6f54SLucas Stach offset += sizeof(u16); 998b5b6f54SLucas Stach break; 1003bfc69abSDean Jenkins } 1013bfc69abSDean Jenkins 1023bfc69abSDean Jenkins if (rx->split_head == true) { 1038b5b6f54SLucas Stach rx->header |= (get_unaligned_le16( 1043bfc69abSDean Jenkins skb->data + offset) << 16); 1058b5b6f54SLucas Stach rx->split_head = false; 1068b5b6f54SLucas Stach offset += sizeof(u16); 1078b5b6f54SLucas Stach } else { 1088b5b6f54SLucas Stach rx->header = get_unaligned_le32(skb->data + 1098b5b6f54SLucas Stach offset); 110b4cdae20SChristian Riesch offset += sizeof(u32); 1118b5b6f54SLucas Stach } 112b4cdae20SChristian Riesch 1137b0378f5SDean Jenkins /* take frame length from Data header 32-bit word */ 1147b0378f5SDean Jenkins size = (u16)(rx->header & 0x7ff); 1157b0378f5SDean Jenkins if (size != ((~rx->header >> 16) & 0x7ff)) { 1168b5b6f54SLucas Stach netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", 1178b5b6f54SLucas Stach rx->header, offset); 1188b5b6f54SLucas Stach return 0; 1198b5b6f54SLucas Stach } 1209a5ccd8eSDean Jenkins if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { 121*b70183dbSstephen hemminger netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", 1229a5ccd8eSDean Jenkins size); 1239a5ccd8eSDean Jenkins return 0; 1249a5ccd8eSDean Jenkins } 1259a5ccd8eSDean Jenkins 1266a570814SDean Jenkins /* Sometimes may fail to get a netdev socket buffer but 1276a570814SDean Jenkins * continue to process the URB socket buffer so that 1286a570814SDean Jenkins * synchronisation of the Ethernet frame Data header 1296a570814SDean Jenkins * word is maintained. 1306a570814SDean Jenkins */ 1317b0378f5SDean Jenkins rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); 132b4cdae20SChristian Riesch 1339a5ccd8eSDean Jenkins rx->remaining = size; 134b4cdae20SChristian Riesch } 135b4cdae20SChristian Riesch 1367b0378f5SDean Jenkins if (rx->remaining > skb->len - offset) { 1377b0378f5SDean Jenkins copy_length = skb->len - offset; 1387b0378f5SDean Jenkins rx->remaining -= copy_length; 1397b0378f5SDean Jenkins } else { 1407b0378f5SDean Jenkins copy_length = rx->remaining; 1417b0378f5SDean Jenkins rx->remaining = 0; 1428b5b6f54SLucas Stach } 143b4cdae20SChristian Riesch 1446a570814SDean Jenkins if (rx->ax_skb) { 1457b0378f5SDean Jenkins data = skb_put(rx->ax_skb, copy_length); 1467b0378f5SDean Jenkins memcpy(data, skb->data + offset, copy_length); 1477b0378f5SDean Jenkins if (!rx->remaining) 1488b5b6f54SLucas Stach usbnet_skb_return(dev, rx->ax_skb); 1496a570814SDean Jenkins } 1508b5b6f54SLucas Stach 1517b0378f5SDean Jenkins offset += (copy_length + 1) & 0xfffe; 152b4cdae20SChristian Riesch } 153b4cdae20SChristian Riesch 154b4cdae20SChristian Riesch if (skb->len != offset) { 1558b5b6f54SLucas Stach netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", 1568b5b6f54SLucas Stach skb->len, offset); 157b4cdae20SChristian Riesch return 0; 158b4cdae20SChristian Riesch } 1598b5b6f54SLucas Stach 160b4cdae20SChristian Riesch return 1; 161b4cdae20SChristian Riesch } 162b4cdae20SChristian Riesch 1638b5b6f54SLucas Stach int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) 1648b5b6f54SLucas Stach { 1658b5b6f54SLucas Stach struct asix_common_private *dp = dev->driver_priv; 1668b5b6f54SLucas Stach struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; 1678b5b6f54SLucas Stach 1688b5b6f54SLucas Stach return asix_rx_fixup_internal(dev, skb, rx); 1698b5b6f54SLucas Stach } 1708b5b6f54SLucas Stach 171607740bcSChristian Riesch struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 172b4cdae20SChristian Riesch gfp_t flags) 173b4cdae20SChristian Riesch { 174b4cdae20SChristian Riesch int padlen; 175b4cdae20SChristian Riesch int headroom = skb_headroom(skb); 176b4cdae20SChristian Riesch int tailroom = skb_tailroom(skb); 177b4cdae20SChristian Riesch u32 packet_len; 178b4cdae20SChristian Riesch u32 padbytes = 0xffff0000; 179b4cdae20SChristian Riesch 180b4cdae20SChristian Riesch padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; 181b4cdae20SChristian Riesch 182b4cdae20SChristian Riesch /* We need to push 4 bytes in front of frame (packet_len) 183b4cdae20SChristian Riesch * and maybe add 4 bytes after the end (if padlen is 4) 184b4cdae20SChristian Riesch * 185b4cdae20SChristian Riesch * Avoid skb_copy_expand() expensive call, using following rules : 186b4cdae20SChristian Riesch * - We are allowed to push 4 bytes in headroom if skb_header_cloned() 187b4cdae20SChristian Riesch * is false (and if we have 4 bytes of headroom) 188b4cdae20SChristian Riesch * - We are allowed to put 4 bytes at tail if skb_cloned() 189b4cdae20SChristian Riesch * is false (and if we have 4 bytes of tailroom) 190b4cdae20SChristian Riesch * 191b4cdae20SChristian Riesch * TCP packets for example are cloned, but skb_header_release() 192b4cdae20SChristian Riesch * was called in tcp stack, allowing us to use headroom for our needs. 193b4cdae20SChristian Riesch */ 194b4cdae20SChristian Riesch if (!skb_header_cloned(skb) && 195b4cdae20SChristian Riesch !(padlen && skb_cloned(skb)) && 196b4cdae20SChristian Riesch headroom + tailroom >= 4 + padlen) { 197b4cdae20SChristian Riesch /* following should not happen, but better be safe */ 198b4cdae20SChristian Riesch if (headroom < 4 || 199b4cdae20SChristian Riesch tailroom < padlen) { 200b4cdae20SChristian Riesch skb->data = memmove(skb->head + 4, skb->data, skb->len); 201b4cdae20SChristian Riesch skb_set_tail_pointer(skb, skb->len); 202b4cdae20SChristian Riesch } 203b4cdae20SChristian Riesch } else { 204b4cdae20SChristian Riesch struct sk_buff *skb2; 205b4cdae20SChristian Riesch 206b4cdae20SChristian Riesch skb2 = skb_copy_expand(skb, 4, padlen, flags); 207b4cdae20SChristian Riesch dev_kfree_skb_any(skb); 208b4cdae20SChristian Riesch skb = skb2; 209b4cdae20SChristian Riesch if (!skb) 210b4cdae20SChristian Riesch return NULL; 211b4cdae20SChristian Riesch } 212b4cdae20SChristian Riesch 213b4cdae20SChristian Riesch packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; 214b4cdae20SChristian Riesch skb_push(skb, 4); 215b4cdae20SChristian Riesch cpu_to_le32s(&packet_len); 216b4cdae20SChristian Riesch skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); 217b4cdae20SChristian Riesch 218b4cdae20SChristian Riesch if (padlen) { 219b4cdae20SChristian Riesch cpu_to_le32s(&padbytes); 220b4cdae20SChristian Riesch memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); 221b4cdae20SChristian Riesch skb_put(skb, sizeof(padbytes)); 222b4cdae20SChristian Riesch } 2231e9e39f4SBen Hutchings 2247a1e890eSBen Hutchings usbnet_set_skb_tx_stats(skb, 1, 0); 225b4cdae20SChristian Riesch return skb; 226b4cdae20SChristian Riesch } 227b4cdae20SChristian Riesch 228607740bcSChristian Riesch int asix_set_sw_mii(struct usbnet *dev) 229b4cdae20SChristian Riesch { 230b4cdae20SChristian Riesch int ret; 231b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); 232b4cdae20SChristian Riesch if (ret < 0) 233b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to enable software MII access\n"); 234b4cdae20SChristian Riesch return ret; 235b4cdae20SChristian Riesch } 236b4cdae20SChristian Riesch 237607740bcSChristian Riesch int asix_set_hw_mii(struct usbnet *dev) 238b4cdae20SChristian Riesch { 239b4cdae20SChristian Riesch int ret; 240b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); 241b4cdae20SChristian Riesch if (ret < 0) 242b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to enable hardware MII access\n"); 243b4cdae20SChristian Riesch return ret; 244b4cdae20SChristian Riesch } 245b4cdae20SChristian Riesch 24616626b0cSChristian Riesch int asix_read_phy_addr(struct usbnet *dev, int internal) 247b4cdae20SChristian Riesch { 24816626b0cSChristian Riesch int offset = (internal ? 1 : 0); 249b4cdae20SChristian Riesch u8 buf[2]; 250b4cdae20SChristian Riesch int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); 251b4cdae20SChristian Riesch 252b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_get_phy_addr()\n"); 253b4cdae20SChristian Riesch 254b4cdae20SChristian Riesch if (ret < 0) { 255b4cdae20SChristian Riesch netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret); 256b4cdae20SChristian Riesch goto out; 257b4cdae20SChristian Riesch } 258b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n", 259b4cdae20SChristian Riesch *((__le16 *)buf)); 26016626b0cSChristian Riesch ret = buf[offset]; 261b4cdae20SChristian Riesch 262b4cdae20SChristian Riesch out: 263b4cdae20SChristian Riesch return ret; 264b4cdae20SChristian Riesch } 265b4cdae20SChristian Riesch 26616626b0cSChristian Riesch int asix_get_phy_addr(struct usbnet *dev) 26716626b0cSChristian Riesch { 26816626b0cSChristian Riesch /* return the address of the internal phy */ 26916626b0cSChristian Riesch return asix_read_phy_addr(dev, 1); 27016626b0cSChristian Riesch } 27116626b0cSChristian Riesch 27216626b0cSChristian Riesch 273607740bcSChristian Riesch int asix_sw_reset(struct usbnet *dev, u8 flags) 274b4cdae20SChristian Riesch { 275b4cdae20SChristian Riesch int ret; 276b4cdae20SChristian Riesch 277b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); 278b4cdae20SChristian Riesch if (ret < 0) 279b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); 280b4cdae20SChristian Riesch 281b4cdae20SChristian Riesch return ret; 282b4cdae20SChristian Riesch } 283b4cdae20SChristian Riesch 284607740bcSChristian Riesch u16 asix_read_rx_ctl(struct usbnet *dev) 285b4cdae20SChristian Riesch { 286b4cdae20SChristian Riesch __le16 v; 287b4cdae20SChristian Riesch int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v); 288b4cdae20SChristian Riesch 289b4cdae20SChristian Riesch if (ret < 0) { 290b4cdae20SChristian Riesch netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); 291b4cdae20SChristian Riesch goto out; 292b4cdae20SChristian Riesch } 293b4cdae20SChristian Riesch ret = le16_to_cpu(v); 294b4cdae20SChristian Riesch out: 295b4cdae20SChristian Riesch return ret; 296b4cdae20SChristian Riesch } 297b4cdae20SChristian Riesch 298607740bcSChristian Riesch int asix_write_rx_ctl(struct usbnet *dev, u16 mode) 299b4cdae20SChristian Riesch { 300b4cdae20SChristian Riesch int ret; 301b4cdae20SChristian Riesch 302b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); 303b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); 304b4cdae20SChristian Riesch if (ret < 0) 305b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", 306b4cdae20SChristian Riesch mode, ret); 307b4cdae20SChristian Riesch 308b4cdae20SChristian Riesch return ret; 309b4cdae20SChristian Riesch } 310b4cdae20SChristian Riesch 311607740bcSChristian Riesch u16 asix_read_medium_status(struct usbnet *dev) 312b4cdae20SChristian Riesch { 313b4cdae20SChristian Riesch __le16 v; 314b4cdae20SChristian Riesch int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); 315b4cdae20SChristian Riesch 316b4cdae20SChristian Riesch if (ret < 0) { 317b4cdae20SChristian Riesch netdev_err(dev->net, "Error reading Medium Status register: %02x\n", 318b4cdae20SChristian Riesch ret); 319b4cdae20SChristian Riesch return ret; /* TODO: callers not checking for error ret */ 320b4cdae20SChristian Riesch } 321b4cdae20SChristian Riesch 322b4cdae20SChristian Riesch return le16_to_cpu(v); 323b4cdae20SChristian Riesch 324b4cdae20SChristian Riesch } 325b4cdae20SChristian Riesch 326607740bcSChristian Riesch int asix_write_medium_mode(struct usbnet *dev, u16 mode) 327b4cdae20SChristian Riesch { 328b4cdae20SChristian Riesch int ret; 329b4cdae20SChristian Riesch 330b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); 331b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); 332b4cdae20SChristian Riesch if (ret < 0) 333b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", 334b4cdae20SChristian Riesch mode, ret); 335b4cdae20SChristian Riesch 336b4cdae20SChristian Riesch return ret; 337b4cdae20SChristian Riesch } 338b4cdae20SChristian Riesch 339607740bcSChristian Riesch int asix_write_gpio(struct usbnet *dev, u16 value, int sleep) 340b4cdae20SChristian Riesch { 341b4cdae20SChristian Riesch int ret; 342b4cdae20SChristian Riesch 343b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); 344b4cdae20SChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); 345b4cdae20SChristian Riesch if (ret < 0) 346b4cdae20SChristian Riesch netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", 347b4cdae20SChristian Riesch value, ret); 348b4cdae20SChristian Riesch 349b4cdae20SChristian Riesch if (sleep) 350b4cdae20SChristian Riesch msleep(sleep); 351b4cdae20SChristian Riesch 352b4cdae20SChristian Riesch return ret; 353b4cdae20SChristian Riesch } 354b4cdae20SChristian Riesch 355b4cdae20SChristian Riesch /* 356b4cdae20SChristian Riesch * AX88772 & AX88178 have a 16-bit RX_CTL value 357b4cdae20SChristian Riesch */ 358607740bcSChristian Riesch void asix_set_multicast(struct net_device *net) 359b4cdae20SChristian Riesch { 360b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 361b4cdae20SChristian Riesch struct asix_data *data = (struct asix_data *)&dev->data; 362b4cdae20SChristian Riesch u16 rx_ctl = AX_DEFAULT_RX_CTL; 363b4cdae20SChristian Riesch 364b4cdae20SChristian Riesch if (net->flags & IFF_PROMISC) { 365b4cdae20SChristian Riesch rx_ctl |= AX_RX_CTL_PRO; 366b4cdae20SChristian Riesch } else if (net->flags & IFF_ALLMULTI || 367b4cdae20SChristian Riesch netdev_mc_count(net) > AX_MAX_MCAST) { 368b4cdae20SChristian Riesch rx_ctl |= AX_RX_CTL_AMALL; 369b4cdae20SChristian Riesch } else if (netdev_mc_empty(net)) { 370b4cdae20SChristian Riesch /* just broadcast and directed */ 371b4cdae20SChristian Riesch } else { 372b4cdae20SChristian Riesch /* We use the 20 byte dev->data 373b4cdae20SChristian Riesch * for our 8 byte filter buffer 374b4cdae20SChristian Riesch * to avoid allocating memory that 375b4cdae20SChristian Riesch * is tricky to free later */ 376b4cdae20SChristian Riesch struct netdev_hw_addr *ha; 377b4cdae20SChristian Riesch u32 crc_bits; 378b4cdae20SChristian Riesch 379b4cdae20SChristian Riesch memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); 380b4cdae20SChristian Riesch 381b4cdae20SChristian Riesch /* Build the multicast hash filter. */ 382b4cdae20SChristian Riesch netdev_for_each_mc_addr(ha, net) { 383b4cdae20SChristian Riesch crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; 384b4cdae20SChristian Riesch data->multi_filter[crc_bits >> 3] |= 385b4cdae20SChristian Riesch 1 << (crc_bits & 7); 386b4cdae20SChristian Riesch } 387b4cdae20SChristian Riesch 388b4cdae20SChristian Riesch asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, 389b4cdae20SChristian Riesch AX_MCAST_FILTER_SIZE, data->multi_filter); 390b4cdae20SChristian Riesch 391b4cdae20SChristian Riesch rx_ctl |= AX_RX_CTL_AM; 392b4cdae20SChristian Riesch } 393b4cdae20SChristian Riesch 394b4cdae20SChristian Riesch asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); 395b4cdae20SChristian Riesch } 396b4cdae20SChristian Riesch 397607740bcSChristian Riesch int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) 398b4cdae20SChristian Riesch { 399b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(netdev); 400b4cdae20SChristian Riesch __le16 res; 401b4cdae20SChristian Riesch 402b4cdae20SChristian Riesch mutex_lock(&dev->phy_mutex); 403b4cdae20SChristian Riesch asix_set_sw_mii(dev); 404b4cdae20SChristian Riesch asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, 405b4cdae20SChristian Riesch (__u16)loc, 2, &res); 406b4cdae20SChristian Riesch asix_set_hw_mii(dev); 407b4cdae20SChristian Riesch mutex_unlock(&dev->phy_mutex); 408b4cdae20SChristian Riesch 409b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", 410b4cdae20SChristian Riesch phy_id, loc, le16_to_cpu(res)); 411b4cdae20SChristian Riesch 412b4cdae20SChristian Riesch return le16_to_cpu(res); 413b4cdae20SChristian Riesch } 414b4cdae20SChristian Riesch 415607740bcSChristian Riesch void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) 416b4cdae20SChristian Riesch { 417b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(netdev); 418b4cdae20SChristian Riesch __le16 res = cpu_to_le16(val); 419b4cdae20SChristian Riesch 420b4cdae20SChristian Riesch netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", 421b4cdae20SChristian Riesch phy_id, loc, val); 422b4cdae20SChristian Riesch mutex_lock(&dev->phy_mutex); 423b4cdae20SChristian Riesch asix_set_sw_mii(dev); 424b4cdae20SChristian Riesch asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); 425b4cdae20SChristian Riesch asix_set_hw_mii(dev); 426b4cdae20SChristian Riesch mutex_unlock(&dev->phy_mutex); 427b4cdae20SChristian Riesch } 428b4cdae20SChristian Riesch 429607740bcSChristian Riesch void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 430b4cdae20SChristian Riesch { 431b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 432b4cdae20SChristian Riesch u8 opt; 433b4cdae20SChristian Riesch 434b4cdae20SChristian Riesch if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { 435b4cdae20SChristian Riesch wolinfo->supported = 0; 436b4cdae20SChristian Riesch wolinfo->wolopts = 0; 437b4cdae20SChristian Riesch return; 438b4cdae20SChristian Riesch } 439b4cdae20SChristian Riesch wolinfo->supported = WAKE_PHY | WAKE_MAGIC; 440b4cdae20SChristian Riesch wolinfo->wolopts = 0; 441b4cdae20SChristian Riesch if (opt & AX_MONITOR_LINK) 442b4cdae20SChristian Riesch wolinfo->wolopts |= WAKE_PHY; 443b4cdae20SChristian Riesch if (opt & AX_MONITOR_MAGIC) 444b4cdae20SChristian Riesch wolinfo->wolopts |= WAKE_MAGIC; 445b4cdae20SChristian Riesch } 446b4cdae20SChristian Riesch 447607740bcSChristian Riesch int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) 448b4cdae20SChristian Riesch { 449b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 450b4cdae20SChristian Riesch u8 opt = 0; 451b4cdae20SChristian Riesch 452b4cdae20SChristian Riesch if (wolinfo->wolopts & WAKE_PHY) 453b4cdae20SChristian Riesch opt |= AX_MONITOR_LINK; 454b4cdae20SChristian Riesch if (wolinfo->wolopts & WAKE_MAGIC) 455b4cdae20SChristian Riesch opt |= AX_MONITOR_MAGIC; 456b4cdae20SChristian Riesch 457b4cdae20SChristian Riesch if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, 458b4cdae20SChristian Riesch opt, 0, 0, NULL) < 0) 459b4cdae20SChristian Riesch return -EINVAL; 460b4cdae20SChristian Riesch 461b4cdae20SChristian Riesch return 0; 462b4cdae20SChristian Riesch } 463b4cdae20SChristian Riesch 464607740bcSChristian Riesch int asix_get_eeprom_len(struct net_device *net) 465b4cdae20SChristian Riesch { 466ceb02c91SChristian Riesch return AX_EEPROM_LEN; 467b4cdae20SChristian Riesch } 468b4cdae20SChristian Riesch 469607740bcSChristian Riesch int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 470607740bcSChristian Riesch u8 *data) 471b4cdae20SChristian Riesch { 472b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 473ceb02c91SChristian Riesch u16 *eeprom_buff; 474ceb02c91SChristian Riesch int first_word, last_word; 475b4cdae20SChristian Riesch int i; 476b4cdae20SChristian Riesch 477ceb02c91SChristian Riesch if (eeprom->len == 0) 478b4cdae20SChristian Riesch return -EINVAL; 479b4cdae20SChristian Riesch 480b4cdae20SChristian Riesch eeprom->magic = AX_EEPROM_MAGIC; 481b4cdae20SChristian Riesch 482ceb02c91SChristian Riesch first_word = eeprom->offset >> 1; 483ceb02c91SChristian Riesch last_word = (eeprom->offset + eeprom->len - 1) >> 1; 484ceb02c91SChristian Riesch 485ceb02c91SChristian Riesch eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), 486ceb02c91SChristian Riesch GFP_KERNEL); 487ceb02c91SChristian Riesch if (!eeprom_buff) 488ceb02c91SChristian Riesch return -ENOMEM; 489ceb02c91SChristian Riesch 490b4cdae20SChristian Riesch /* ax8817x returns 2 bytes from eeprom on read */ 491ceb02c91SChristian Riesch for (i = first_word; i <= last_word; i++) { 492ceb02c91SChristian Riesch if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, 493ceb02c91SChristian Riesch &(eeprom_buff[i - first_word])) < 0) { 494ceb02c91SChristian Riesch kfree(eeprom_buff); 495ceb02c91SChristian Riesch return -EIO; 496b4cdae20SChristian Riesch } 497ceb02c91SChristian Riesch } 498ceb02c91SChristian Riesch 499ceb02c91SChristian Riesch memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); 500ceb02c91SChristian Riesch kfree(eeprom_buff); 501b4cdae20SChristian Riesch return 0; 502b4cdae20SChristian Riesch } 503b4cdae20SChristian Riesch 504cb7b24cdSChristian Riesch int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, 505cb7b24cdSChristian Riesch u8 *data) 506cb7b24cdSChristian Riesch { 507cb7b24cdSChristian Riesch struct usbnet *dev = netdev_priv(net); 508cb7b24cdSChristian Riesch u16 *eeprom_buff; 509cb7b24cdSChristian Riesch int first_word, last_word; 510cb7b24cdSChristian Riesch int i; 511cb7b24cdSChristian Riesch int ret; 512cb7b24cdSChristian Riesch 513cb7b24cdSChristian Riesch netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n", 514cb7b24cdSChristian Riesch eeprom->len, eeprom->offset, eeprom->magic); 515cb7b24cdSChristian Riesch 516cb7b24cdSChristian Riesch if (eeprom->len == 0) 517cb7b24cdSChristian Riesch return -EINVAL; 518cb7b24cdSChristian Riesch 519cb7b24cdSChristian Riesch if (eeprom->magic != AX_EEPROM_MAGIC) 520cb7b24cdSChristian Riesch return -EINVAL; 521cb7b24cdSChristian Riesch 522cb7b24cdSChristian Riesch first_word = eeprom->offset >> 1; 523cb7b24cdSChristian Riesch last_word = (eeprom->offset + eeprom->len - 1) >> 1; 524cb7b24cdSChristian Riesch 525cb7b24cdSChristian Riesch eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), 526cb7b24cdSChristian Riesch GFP_KERNEL); 527cb7b24cdSChristian Riesch if (!eeprom_buff) 528cb7b24cdSChristian Riesch return -ENOMEM; 529cb7b24cdSChristian Riesch 530cb7b24cdSChristian Riesch /* align data to 16 bit boundaries, read the missing data from 531cb7b24cdSChristian Riesch the EEPROM */ 532cb7b24cdSChristian Riesch if (eeprom->offset & 1) { 533cb7b24cdSChristian Riesch ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, 534cb7b24cdSChristian Riesch &(eeprom_buff[0])); 535cb7b24cdSChristian Riesch if (ret < 0) { 536cb7b24cdSChristian Riesch netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); 537cb7b24cdSChristian Riesch goto free; 538cb7b24cdSChristian Riesch } 539cb7b24cdSChristian Riesch } 540cb7b24cdSChristian Riesch 541cb7b24cdSChristian Riesch if ((eeprom->offset + eeprom->len) & 1) { 542cb7b24cdSChristian Riesch ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, 543cb7b24cdSChristian Riesch &(eeprom_buff[last_word - first_word])); 544cb7b24cdSChristian Riesch if (ret < 0) { 545cb7b24cdSChristian Riesch netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); 546cb7b24cdSChristian Riesch goto free; 547cb7b24cdSChristian Riesch } 548cb7b24cdSChristian Riesch } 549cb7b24cdSChristian Riesch 550cb7b24cdSChristian Riesch memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); 551cb7b24cdSChristian Riesch 552cb7b24cdSChristian Riesch /* write data to EEPROM */ 553cb7b24cdSChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL); 554cb7b24cdSChristian Riesch if (ret < 0) { 555cb7b24cdSChristian Riesch netdev_err(net, "Failed to enable EEPROM write\n"); 556cb7b24cdSChristian Riesch goto free; 557cb7b24cdSChristian Riesch } 558cb7b24cdSChristian Riesch msleep(20); 559cb7b24cdSChristian Riesch 560cb7b24cdSChristian Riesch for (i = first_word; i <= last_word; i++) { 561cb7b24cdSChristian Riesch netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", 562cb7b24cdSChristian Riesch i, eeprom_buff[i - first_word]); 563cb7b24cdSChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, 564cb7b24cdSChristian Riesch eeprom_buff[i - first_word], 0, NULL); 565cb7b24cdSChristian Riesch if (ret < 0) { 566cb7b24cdSChristian Riesch netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", 567cb7b24cdSChristian Riesch i); 568cb7b24cdSChristian Riesch goto free; 569cb7b24cdSChristian Riesch } 570cb7b24cdSChristian Riesch msleep(20); 571cb7b24cdSChristian Riesch } 572cb7b24cdSChristian Riesch 573cb7b24cdSChristian Riesch ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL); 574cb7b24cdSChristian Riesch if (ret < 0) { 575cb7b24cdSChristian Riesch netdev_err(net, "Failed to disable EEPROM write\n"); 576cb7b24cdSChristian Riesch goto free; 577cb7b24cdSChristian Riesch } 578cb7b24cdSChristian Riesch 579cb7b24cdSChristian Riesch ret = 0; 580cb7b24cdSChristian Riesch free: 581cb7b24cdSChristian Riesch kfree(eeprom_buff); 582cb7b24cdSChristian Riesch return ret; 583cb7b24cdSChristian Riesch } 584cb7b24cdSChristian Riesch 585607740bcSChristian Riesch void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) 586b4cdae20SChristian Riesch { 587b4cdae20SChristian Riesch /* Inherit standard device info */ 588b4cdae20SChristian Riesch usbnet_get_drvinfo(net, info); 5897826d43fSJiri Pirko strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); 5907826d43fSJiri Pirko strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); 591b4cdae20SChristian Riesch } 592b4cdae20SChristian Riesch 593607740bcSChristian Riesch int asix_set_mac_address(struct net_device *net, void *p) 594b4cdae20SChristian Riesch { 595b4cdae20SChristian Riesch struct usbnet *dev = netdev_priv(net); 596b4cdae20SChristian Riesch struct asix_data *data = (struct asix_data *)&dev->data; 597b4cdae20SChristian Riesch struct sockaddr *addr = p; 598b4cdae20SChristian Riesch 599b4cdae20SChristian Riesch if (netif_running(net)) 600b4cdae20SChristian Riesch return -EBUSY; 601b4cdae20SChristian Riesch if (!is_valid_ether_addr(addr->sa_data)) 602b4cdae20SChristian Riesch return -EADDRNOTAVAIL; 603b4cdae20SChristian Riesch 604b4cdae20SChristian Riesch memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); 605b4cdae20SChristian Riesch 606b4cdae20SChristian Riesch /* We use the 20 byte dev->data 607b4cdae20SChristian Riesch * for our 6 byte mac buffer 608b4cdae20SChristian Riesch * to avoid allocating memory that 609b4cdae20SChristian Riesch * is tricky to free later */ 610b4cdae20SChristian Riesch memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); 611b4cdae20SChristian Riesch asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, 612b4cdae20SChristian Riesch data->mac_addr); 613b4cdae20SChristian Riesch 614b4cdae20SChristian Riesch return 0; 615b4cdae20SChristian Riesch } 616