0001
0002 #include <linux/module.h>
0003 #include <linux/netdevice.h>
0004 #include <linux/mii.h>
0005 #include <linux/usb.h>
0006 #include <linux/usb/cdc.h>
0007 #include <linux/usb/usbnet.h>
0008 #include <linux/usb/r8152.h>
0009
0010 #define OCP_BASE 0xe86c
0011
0012 static int pla_read_word(struct usbnet *dev, u16 index)
0013 {
0014 u16 byen = BYTE_EN_WORD;
0015 u8 shift = index & 2;
0016 __le32 tmp;
0017 int ret;
0018
0019 if (shift)
0020 byen <<= shift;
0021
0022 index &= ~3;
0023
0024 ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index,
0025 MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
0026 if (ret < 0)
0027 goto out;
0028
0029 ret = __le32_to_cpu(tmp);
0030 ret >>= (shift * 8);
0031 ret &= 0xffff;
0032
0033 out:
0034 return ret;
0035 }
0036
0037 static int pla_write_word(struct usbnet *dev, u16 index, u32 data)
0038 {
0039 u32 mask = 0xffff;
0040 u16 byen = BYTE_EN_WORD;
0041 u8 shift = index & 2;
0042 __le32 tmp;
0043 int ret;
0044
0045 data &= mask;
0046
0047 if (shift) {
0048 byen <<= shift;
0049 mask <<= (shift * 8);
0050 data <<= (shift * 8);
0051 }
0052
0053 index &= ~3;
0054
0055 ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index,
0056 MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
0057
0058 if (ret < 0)
0059 goto out;
0060
0061 data |= __le32_to_cpu(tmp) & ~mask;
0062 tmp = __cpu_to_le32(data);
0063
0064 ret = usbnet_write_cmd(dev, RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, index,
0065 MCU_TYPE_PLA | byen, &tmp, sizeof(tmp));
0066
0067 out:
0068 return ret;
0069 }
0070
0071 static int r8153_ecm_mdio_read(struct net_device *netdev, int phy_id, int reg)
0072 {
0073 struct usbnet *dev = netdev_priv(netdev);
0074 int ret;
0075
0076 ret = pla_write_word(dev, OCP_BASE, 0xa000);
0077 if (ret < 0)
0078 goto out;
0079
0080 ret = pla_read_word(dev, 0xb400 + reg * 2);
0081
0082 out:
0083 return ret;
0084 }
0085
0086 static void r8153_ecm_mdio_write(struct net_device *netdev, int phy_id, int reg, int val)
0087 {
0088 struct usbnet *dev = netdev_priv(netdev);
0089 int ret;
0090
0091 ret = pla_write_word(dev, OCP_BASE, 0xa000);
0092 if (ret < 0)
0093 return;
0094
0095 ret = pla_write_word(dev, 0xb400 + reg * 2, val);
0096 }
0097
0098 static int r8153_bind(struct usbnet *dev, struct usb_interface *intf)
0099 {
0100 int status;
0101
0102 status = usbnet_cdc_bind(dev, intf);
0103 if (status < 0)
0104 return status;
0105
0106 dev->mii.dev = dev->net;
0107 dev->mii.mdio_read = r8153_ecm_mdio_read;
0108 dev->mii.mdio_write = r8153_ecm_mdio_write;
0109 dev->mii.reg_num_mask = 0x1f;
0110 dev->mii.supports_gmii = 1;
0111
0112 return status;
0113 }
0114
0115 static const struct driver_info r8153_info = {
0116 .description = "RTL8153 ECM Device",
0117 .flags = FLAG_ETHER,
0118 .bind = r8153_bind,
0119 .unbind = usbnet_cdc_unbind,
0120 .status = usbnet_cdc_status,
0121 .manage_power = usbnet_manage_power,
0122 };
0123
0124 static const struct usb_device_id products[] = {
0125
0126 {
0127 USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_REALTEK, 0x8153, USB_CLASS_COMM,
0128 USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
0129 .driver_info = (unsigned long)&r8153_info,
0130 },
0131
0132
0133 {
0134 USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_LENOVO, 0x721e, USB_CLASS_COMM,
0135 USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
0136 .driver_info = (unsigned long)&r8153_info,
0137 },
0138
0139 { },
0140 };
0141 MODULE_DEVICE_TABLE(usb, products);
0142
0143 static int rtl8153_ecm_probe(struct usb_interface *intf,
0144 const struct usb_device_id *id)
0145 {
0146 #if IS_REACHABLE(CONFIG_USB_RTL8152)
0147 if (rtl8152_get_version(intf))
0148 return -ENODEV;
0149 #endif
0150
0151 return usbnet_probe(intf, id);
0152 }
0153
0154 static struct usb_driver r8153_ecm_driver = {
0155 .name = "r8153_ecm",
0156 .id_table = products,
0157 .probe = rtl8153_ecm_probe,
0158 .disconnect = usbnet_disconnect,
0159 .suspend = usbnet_suspend,
0160 .resume = usbnet_resume,
0161 .reset_resume = usbnet_resume,
0162 .supports_autosuspend = 1,
0163 .disable_hub_initiated_lpm = 1,
0164 };
0165
0166 module_usb_driver(r8153_ecm_driver);
0167
0168 MODULE_AUTHOR("Hayes Wang");
0169 MODULE_DESCRIPTION("Realtek USB ECM device");
0170 MODULE_LICENSE("GPL");