117364b80SDmitry Bezrukov // SPDX-License-Identifier: GPL-2.0-or-later 217364b80SDmitry Bezrukov /* Aquantia Corp. Aquantia AQtion USB to 5GbE Controller 317364b80SDmitry Bezrukov * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com> 417364b80SDmitry Bezrukov * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> 517364b80SDmitry Bezrukov * Copyright (C) 2002-2003 TiVo Inc. 617364b80SDmitry Bezrukov * Copyright (C) 2017-2018 ASIX 717364b80SDmitry Bezrukov * Copyright (C) 2018 Aquantia Corp. 817364b80SDmitry Bezrukov */ 917364b80SDmitry Bezrukov 1017364b80SDmitry Bezrukov #include <linux/module.h> 1117364b80SDmitry Bezrukov #include <linux/netdevice.h> 1217364b80SDmitry Bezrukov #include <linux/mii.h> 1317364b80SDmitry Bezrukov #include <linux/usb.h> 1417364b80SDmitry Bezrukov #include <linux/usb/cdc.h> 1517364b80SDmitry Bezrukov #include <linux/usb/usbnet.h> 1617364b80SDmitry Bezrukov 17619fcb44SDmitry Bezrukov #include "aqc111.h" 18619fcb44SDmitry Bezrukov 19619fcb44SDmitry Bezrukov static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, 20619fcb44SDmitry Bezrukov u16 index, u16 size, void *data) 21619fcb44SDmitry Bezrukov { 22619fcb44SDmitry Bezrukov int ret; 23619fcb44SDmitry Bezrukov 24619fcb44SDmitry Bezrukov ret = usbnet_read_cmd_nopm(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | 25619fcb44SDmitry Bezrukov USB_RECIP_DEVICE, value, index, data, size); 26619fcb44SDmitry Bezrukov 27619fcb44SDmitry Bezrukov if (unlikely(ret < 0)) 28619fcb44SDmitry Bezrukov netdev_warn(dev->net, 29619fcb44SDmitry Bezrukov "Failed to read(0x%x) reg index 0x%04x: %d\n", 30619fcb44SDmitry Bezrukov cmd, index, ret); 31619fcb44SDmitry Bezrukov 32619fcb44SDmitry Bezrukov return ret; 33619fcb44SDmitry Bezrukov } 34619fcb44SDmitry Bezrukov 35619fcb44SDmitry Bezrukov static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value, 36619fcb44SDmitry Bezrukov u16 index, u16 size, void *data) 37619fcb44SDmitry Bezrukov { 38619fcb44SDmitry Bezrukov int ret; 39619fcb44SDmitry Bezrukov 40619fcb44SDmitry Bezrukov ret = usbnet_read_cmd(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | 41619fcb44SDmitry Bezrukov USB_RECIP_DEVICE, value, index, data, size); 42619fcb44SDmitry Bezrukov 43619fcb44SDmitry Bezrukov if (unlikely(ret < 0)) 44619fcb44SDmitry Bezrukov netdev_warn(dev->net, 45619fcb44SDmitry Bezrukov "Failed to read(0x%x) reg index 0x%04x: %d\n", 46619fcb44SDmitry Bezrukov cmd, index, ret); 47619fcb44SDmitry Bezrukov 48619fcb44SDmitry Bezrukov return ret; 49619fcb44SDmitry Bezrukov } 50619fcb44SDmitry Bezrukov 51*f3aa095aSDmitry Bezrukov static int aqc111_read16_cmd(struct usbnet *dev, u8 cmd, u16 value, 52*f3aa095aSDmitry Bezrukov u16 index, u16 *data) 53*f3aa095aSDmitry Bezrukov { 54*f3aa095aSDmitry Bezrukov int ret = 0; 55*f3aa095aSDmitry Bezrukov 56*f3aa095aSDmitry Bezrukov ret = aqc111_read_cmd(dev, cmd, value, index, sizeof(*data), data); 57*f3aa095aSDmitry Bezrukov le16_to_cpus(data); 58*f3aa095aSDmitry Bezrukov 59*f3aa095aSDmitry Bezrukov return ret; 60*f3aa095aSDmitry Bezrukov } 61*f3aa095aSDmitry Bezrukov 62619fcb44SDmitry Bezrukov static int __aqc111_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, 63619fcb44SDmitry Bezrukov u16 value, u16 index, u16 size, const void *data) 64619fcb44SDmitry Bezrukov { 65619fcb44SDmitry Bezrukov int err = -ENOMEM; 66619fcb44SDmitry Bezrukov void *buf = NULL; 67619fcb44SDmitry Bezrukov 68619fcb44SDmitry Bezrukov netdev_dbg(dev->net, 69619fcb44SDmitry Bezrukov "%s cmd=%#x reqtype=%#x value=%#x index=%#x size=%d\n", 70619fcb44SDmitry Bezrukov __func__, cmd, reqtype, value, index, size); 71619fcb44SDmitry Bezrukov 72619fcb44SDmitry Bezrukov if (data) { 73619fcb44SDmitry Bezrukov buf = kmemdup(data, size, GFP_KERNEL); 74619fcb44SDmitry Bezrukov if (!buf) 75619fcb44SDmitry Bezrukov goto out; 76619fcb44SDmitry Bezrukov } 77619fcb44SDmitry Bezrukov 78619fcb44SDmitry Bezrukov err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), 79619fcb44SDmitry Bezrukov cmd, reqtype, value, index, buf, size, 80619fcb44SDmitry Bezrukov (cmd == AQ_PHY_POWER) ? AQ_USB_PHY_SET_TIMEOUT : 81619fcb44SDmitry Bezrukov AQ_USB_SET_TIMEOUT); 82619fcb44SDmitry Bezrukov 83619fcb44SDmitry Bezrukov if (unlikely(err < 0)) 84619fcb44SDmitry Bezrukov netdev_warn(dev->net, 85619fcb44SDmitry Bezrukov "Failed to write(0x%x) reg index 0x%04x: %d\n", 86619fcb44SDmitry Bezrukov cmd, index, err); 87619fcb44SDmitry Bezrukov kfree(buf); 88619fcb44SDmitry Bezrukov 89619fcb44SDmitry Bezrukov out: 90619fcb44SDmitry Bezrukov return err; 91619fcb44SDmitry Bezrukov } 92619fcb44SDmitry Bezrukov 93619fcb44SDmitry Bezrukov static int aqc111_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, 94619fcb44SDmitry Bezrukov u16 index, u16 size, void *data) 95619fcb44SDmitry Bezrukov { 96619fcb44SDmitry Bezrukov int ret; 97619fcb44SDmitry Bezrukov 98619fcb44SDmitry Bezrukov ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | 99619fcb44SDmitry Bezrukov USB_RECIP_DEVICE, value, index, size, data); 100619fcb44SDmitry Bezrukov 101619fcb44SDmitry Bezrukov return ret; 102619fcb44SDmitry Bezrukov } 103619fcb44SDmitry Bezrukov 104619fcb44SDmitry Bezrukov static int aqc111_write_cmd(struct usbnet *dev, u8 cmd, u16 value, 105619fcb44SDmitry Bezrukov u16 index, u16 size, void *data) 106619fcb44SDmitry Bezrukov { 107619fcb44SDmitry Bezrukov int ret; 108619fcb44SDmitry Bezrukov 109619fcb44SDmitry Bezrukov if (usb_autopm_get_interface(dev->intf) < 0) 110619fcb44SDmitry Bezrukov return -ENODEV; 111619fcb44SDmitry Bezrukov 112619fcb44SDmitry Bezrukov ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | 113619fcb44SDmitry Bezrukov USB_RECIP_DEVICE, value, index, size, data); 114619fcb44SDmitry Bezrukov 115619fcb44SDmitry Bezrukov usb_autopm_put_interface(dev->intf); 116619fcb44SDmitry Bezrukov 117619fcb44SDmitry Bezrukov return ret; 118619fcb44SDmitry Bezrukov } 119619fcb44SDmitry Bezrukov 120*f3aa095aSDmitry Bezrukov static int aqc111_write16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, 121*f3aa095aSDmitry Bezrukov u16 index, u16 *data) 122*f3aa095aSDmitry Bezrukov { 123*f3aa095aSDmitry Bezrukov u16 tmp = *data; 124*f3aa095aSDmitry Bezrukov 125*f3aa095aSDmitry Bezrukov cpu_to_le16s(&tmp); 126*f3aa095aSDmitry Bezrukov 127*f3aa095aSDmitry Bezrukov return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp); 128*f3aa095aSDmitry Bezrukov } 129*f3aa095aSDmitry Bezrukov 130*f3aa095aSDmitry Bezrukov static int aqc111_write16_cmd(struct usbnet *dev, u8 cmd, u16 value, 131*f3aa095aSDmitry Bezrukov u16 index, u16 *data) 132*f3aa095aSDmitry Bezrukov { 133*f3aa095aSDmitry Bezrukov u16 tmp = *data; 134*f3aa095aSDmitry Bezrukov 135*f3aa095aSDmitry Bezrukov cpu_to_le16s(&tmp); 136*f3aa095aSDmitry Bezrukov 137*f3aa095aSDmitry Bezrukov return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp); 138*f3aa095aSDmitry Bezrukov } 139*f3aa095aSDmitry Bezrukov 1407cea2d40SDmitry Bezrukov static const struct net_device_ops aqc111_netdev_ops = { 1417cea2d40SDmitry Bezrukov .ndo_open = usbnet_open, 1427cea2d40SDmitry Bezrukov .ndo_stop = usbnet_stop, 1437cea2d40SDmitry Bezrukov }; 1447cea2d40SDmitry Bezrukov 1457cea2d40SDmitry Bezrukov static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf) 1467cea2d40SDmitry Bezrukov { 1477cea2d40SDmitry Bezrukov struct usb_device *udev = interface_to_usbdev(intf); 1487cea2d40SDmitry Bezrukov int ret; 1497cea2d40SDmitry Bezrukov 1507cea2d40SDmitry Bezrukov /* Check if vendor configuration */ 1517cea2d40SDmitry Bezrukov if (udev->actconfig->desc.bConfigurationValue != 1) { 1527cea2d40SDmitry Bezrukov usb_driver_set_configuration(udev, 1); 1537cea2d40SDmitry Bezrukov return -ENODEV; 1547cea2d40SDmitry Bezrukov } 1557cea2d40SDmitry Bezrukov 1567cea2d40SDmitry Bezrukov usb_reset_configuration(dev->udev); 1577cea2d40SDmitry Bezrukov 1587cea2d40SDmitry Bezrukov ret = usbnet_get_endpoints(dev, intf); 1597cea2d40SDmitry Bezrukov if (ret < 0) { 1607cea2d40SDmitry Bezrukov netdev_dbg(dev->net, "usbnet_get_endpoints failed"); 1617cea2d40SDmitry Bezrukov return ret; 1627cea2d40SDmitry Bezrukov } 1637cea2d40SDmitry Bezrukov 1647cea2d40SDmitry Bezrukov dev->net->netdev_ops = &aqc111_netdev_ops; 1657cea2d40SDmitry Bezrukov 1667cea2d40SDmitry Bezrukov return 0; 1677cea2d40SDmitry Bezrukov } 1687cea2d40SDmitry Bezrukov 1697cea2d40SDmitry Bezrukov static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf) 1707cea2d40SDmitry Bezrukov { 171*f3aa095aSDmitry Bezrukov u16 reg16; 172*f3aa095aSDmitry Bezrukov 173*f3aa095aSDmitry Bezrukov /* Force bz */ 174*f3aa095aSDmitry Bezrukov reg16 = SFR_PHYPWR_RSTCTL_BZ; 175*f3aa095aSDmitry Bezrukov aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL, 176*f3aa095aSDmitry Bezrukov 2, ®16); 177*f3aa095aSDmitry Bezrukov reg16 = 0; 178*f3aa095aSDmitry Bezrukov aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL, 179*f3aa095aSDmitry Bezrukov 2, ®16); 180*f3aa095aSDmitry Bezrukov } 181*f3aa095aSDmitry Bezrukov 182*f3aa095aSDmitry Bezrukov static int aqc111_reset(struct usbnet *dev) 183*f3aa095aSDmitry Bezrukov { 184*f3aa095aSDmitry Bezrukov u8 reg8 = 0; 185*f3aa095aSDmitry Bezrukov 186*f3aa095aSDmitry Bezrukov reg8 = 0xFF; 187*f3aa095aSDmitry Bezrukov aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, ®8); 188*f3aa095aSDmitry Bezrukov 189*f3aa095aSDmitry Bezrukov reg8 = 0x0; 190*f3aa095aSDmitry Bezrukov aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_SWP_CTRL, 1, 1, ®8); 191*f3aa095aSDmitry Bezrukov 192*f3aa095aSDmitry Bezrukov aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, ®8); 193*f3aa095aSDmitry Bezrukov reg8 &= ~(SFR_MONITOR_MODE_EPHYRW | SFR_MONITOR_MODE_RWLC | 194*f3aa095aSDmitry Bezrukov SFR_MONITOR_MODE_RWMP | SFR_MONITOR_MODE_RWWF | 195*f3aa095aSDmitry Bezrukov SFR_MONITOR_MODE_RW_FLAG); 196*f3aa095aSDmitry Bezrukov aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, ®8); 197*f3aa095aSDmitry Bezrukov 198*f3aa095aSDmitry Bezrukov return 0; 199*f3aa095aSDmitry Bezrukov } 200*f3aa095aSDmitry Bezrukov 201*f3aa095aSDmitry Bezrukov static int aqc111_stop(struct usbnet *dev) 202*f3aa095aSDmitry Bezrukov { 203*f3aa095aSDmitry Bezrukov u16 reg16 = 0; 204*f3aa095aSDmitry Bezrukov 205*f3aa095aSDmitry Bezrukov aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, 206*f3aa095aSDmitry Bezrukov 2, ®16); 207*f3aa095aSDmitry Bezrukov reg16 &= ~SFR_MEDIUM_RECEIVE_EN; 208*f3aa095aSDmitry Bezrukov aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE, 209*f3aa095aSDmitry Bezrukov 2, ®16); 210*f3aa095aSDmitry Bezrukov reg16 = 0; 211*f3aa095aSDmitry Bezrukov aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, ®16); 212*f3aa095aSDmitry Bezrukov 213*f3aa095aSDmitry Bezrukov return 0; 2147cea2d40SDmitry Bezrukov } 2157cea2d40SDmitry Bezrukov 21617364b80SDmitry Bezrukov static const struct driver_info aqc111_info = { 21717364b80SDmitry Bezrukov .description = "Aquantia AQtion USB to 5GbE Controller", 2187cea2d40SDmitry Bezrukov .bind = aqc111_bind, 2197cea2d40SDmitry Bezrukov .unbind = aqc111_unbind, 220*f3aa095aSDmitry Bezrukov .reset = aqc111_reset, 221*f3aa095aSDmitry Bezrukov .stop = aqc111_stop, 22217364b80SDmitry Bezrukov }; 22317364b80SDmitry Bezrukov 22417364b80SDmitry Bezrukov #define AQC111_USB_ETH_DEV(vid, pid, table) \ 22517364b80SDmitry Bezrukov USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_VENDOR_SPEC), \ 22617364b80SDmitry Bezrukov .driver_info = (unsigned long)&(table) \ 22717364b80SDmitry Bezrukov }, \ 22817364b80SDmitry Bezrukov { \ 22917364b80SDmitry Bezrukov USB_DEVICE_AND_INTERFACE_INFO((vid), (pid), \ 23017364b80SDmitry Bezrukov USB_CLASS_COMM, \ 23117364b80SDmitry Bezrukov USB_CDC_SUBCLASS_ETHERNET, \ 23217364b80SDmitry Bezrukov USB_CDC_PROTO_NONE), \ 23317364b80SDmitry Bezrukov .driver_info = (unsigned long)&(table), 23417364b80SDmitry Bezrukov 23517364b80SDmitry Bezrukov static const struct usb_device_id products[] = { 23617364b80SDmitry Bezrukov {AQC111_USB_ETH_DEV(0x2eca, 0xc101, aqc111_info)}, 23717364b80SDmitry Bezrukov { },/* END */ 23817364b80SDmitry Bezrukov }; 23917364b80SDmitry Bezrukov MODULE_DEVICE_TABLE(usb, products); 24017364b80SDmitry Bezrukov 24117364b80SDmitry Bezrukov static struct usb_driver aq_driver = { 24217364b80SDmitry Bezrukov .name = "aqc111", 24317364b80SDmitry Bezrukov .id_table = products, 24417364b80SDmitry Bezrukov .probe = usbnet_probe, 24517364b80SDmitry Bezrukov .disconnect = usbnet_disconnect, 24617364b80SDmitry Bezrukov }; 24717364b80SDmitry Bezrukov 24817364b80SDmitry Bezrukov module_usb_driver(aq_driver); 24917364b80SDmitry Bezrukov 25017364b80SDmitry Bezrukov MODULE_DESCRIPTION("Aquantia AQtion USB to 5/2.5GbE Controllers"); 25117364b80SDmitry Bezrukov MODULE_LICENSE("GPL"); 252