171 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2#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 0xe86c11 12static 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 33out:34 return ret;35}36 37static 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 67out:68 return ret;69}70 71static 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 82out:83 return ret;84}85 86static 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 98static 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 115static 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 124static 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};141MODULE_DEVICE_TABLE(usb, products);142 143static 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#endif150 151 return usbnet_probe(intf, id);152}153 154static 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 166module_usb_driver(r8153_ecm_driver);167 168MODULE_AUTHOR("Hayes Wang");169MODULE_DESCRIPTION("Realtek USB ECM device");170MODULE_LICENSE("GPL");171