1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <linux/module.h> 3 #include <linux/netdevice.h> 4 #include <linux/mii.h> 5 #include <linux/usb.h> 6 #include <linux/usb/cdc.h> 7 #include <linux/usb/usbnet.h> 8 #include <linux/usb/r8152.h> 9 10 #define OCP_BASE 0xe86c 11 12 static int pla_read_word(struct usbnet *dev, u16 index) 13 { 14 u16 byen = BYTE_EN_WORD; 15 u8 shift = index & 2; 16 __le32 tmp; 17 int ret; 18 19 if (shift) 20 byen <<= shift; 21 22 index &= ~3; 23 24 ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index, 25 MCU_TYPE_PLA | byen, &tmp, sizeof(tmp)); 26 if (ret < 0) 27 goto out; 28 29 ret = __le32_to_cpu(tmp); 30 ret >>= (shift * 8); 31 ret &= 0xffff; 32 33 out: 34 return ret; 35 } 36 37 static int pla_write_word(struct usbnet *dev, u16 index, u32 data) 38 { 39 u32 mask = 0xffff; 40 u16 byen = BYTE_EN_WORD; 41 u8 shift = index & 2; 42 __le32 tmp; 43 int ret; 44 45 data &= mask; 46 47 if (shift) { 48 byen <<= shift; 49 mask <<= (shift * 8); 50 data <<= (shift * 8); 51 } 52 53 index &= ~3; 54 55 ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index, 56 MCU_TYPE_PLA | byen, &tmp, sizeof(tmp)); 57 58 if (ret < 0) 59 goto out; 60 61 data |= __le32_to_cpu(tmp) & ~mask; 62 tmp = __cpu_to_le32(data); 63 64 ret = usbnet_write_cmd(dev, RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, index, 65 MCU_TYPE_PLA | byen, &tmp, sizeof(tmp)); 66 67 out: 68 return ret; 69 } 70 71 static int r8153_ecm_mdio_read(struct net_device *netdev, int phy_id, int reg) 72 { 73 struct usbnet *dev = netdev_priv(netdev); 74 int ret; 75 76 ret = pla_write_word(dev, OCP_BASE, 0xa000); 77 if (ret < 0) 78 goto out; 79 80 ret = pla_read_word(dev, 0xb400 + reg * 2); 81 82 out: 83 return ret; 84 } 85 86 static void r8153_ecm_mdio_write(struct net_device *netdev, int phy_id, int reg, int val) 87 { 88 struct usbnet *dev = netdev_priv(netdev); 89 int ret; 90 91 ret = pla_write_word(dev, OCP_BASE, 0xa000); 92 if (ret < 0) 93 return; 94 95 ret = pla_write_word(dev, 0xb400 + reg * 2, val); 96 } 97 98 static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) 99 { 100 int status; 101 102 status = usbnet_cdc_bind(dev, intf); 103 if (status < 0) 104 return status; 105 106 dev->mii.dev = dev->net; 107 dev->mii.mdio_read = r8153_ecm_mdio_read; 108 dev->mii.mdio_write = r8153_ecm_mdio_write; 109 dev->mii.reg_num_mask = 0x1f; 110 dev->mii.supports_gmii = 1; 111 112 return status; 113 } 114 115 static const struct driver_info r8153_info = { 116 .description = "RTL8153 ECM Device", 117 .flags = FLAG_ETHER, 118 .bind = r8153_bind, 119 .unbind = usbnet_cdc_unbind, 120 .status = usbnet_cdc_status, 121 .manage_power = usbnet_manage_power, 122 }; 123 124 static const struct usb_device_id products[] = { 125 /* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */ 126 { 127 USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_REALTEK, 0x8153, USB_CLASS_COMM, 128 USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 129 .driver_info = (unsigned long)&r8153_info, 130 }, 131 132 /* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */ 133 { 134 USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_LENOVO, 0x721e, USB_CLASS_COMM, 135 USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 136 .driver_info = (unsigned long)&r8153_info, 137 }, 138 139 { }, /* END */ 140 }; 141 MODULE_DEVICE_TABLE(usb, products); 142 143 static int rtl8153_ecm_probe(struct usb_interface *intf, 144 const struct usb_device_id *id) 145 { 146 #if IS_REACHABLE(CONFIG_USB_RTL8152) 147 if (rtl8152_get_version(intf)) 148 return -ENODEV; 149 #endif 150 151 return usbnet_probe(intf, id); 152 } 153 154 static struct usb_driver r8153_ecm_driver = { 155 .name = "r8153_ecm", 156 .id_table = products, 157 .probe = rtl8153_ecm_probe, 158 .disconnect = usbnet_disconnect, 159 .suspend = usbnet_suspend, 160 .resume = usbnet_resume, 161 .reset_resume = usbnet_resume, 162 .supports_autosuspend = 1, 163 .disable_hub_initiated_lpm = 1, 164 }; 165 166 module_usb_driver(r8153_ecm_driver); 167 168 MODULE_AUTHOR("Hayes Wang"); 169 MODULE_DESCRIPTION("Realtek USB ECM device"); 170 MODULE_LICENSE("GPL"); 171