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