c073f666 |
1 | #include <linux/module.h> |
2 | #include <linux/netdevice.h> |
3 | #include <linux/mii.h> |
4 | #include <linux/usb.h> |
5 | #include <linux/usb/cdc.h> |
6 | #include <linux/usb/usbnet.h> |
7 | |
8 | #define RTL815x_REQT_READ 0xc0 |
9 | #define RTL815x_REQT_WRITE 0x40 |
10 | #define RTL815x_REQ_GET_REGS 0x05 |
11 | #define RTL815x_REQ_SET_REGS 0x05 |
12 | |
13 | #define MCU_TYPE_PLA 0x0100 |
14 | #define OCP_BASE 0xe86c |
15 | #define BASE_MII 0xa400 |
16 | |
17 | #define BYTE_EN_DWORD 0xff |
18 | #define BYTE_EN_WORD 0x33 |
19 | #define BYTE_EN_BYTE 0x11 |
20 | |
21 | #define R815x_PHY_ID 32 |
22 | #define REALTEK_VENDOR_ID 0x0bda |
23 | |
24 | |
25 | static int pla_read_word(struct usb_device *udev, u16 index) |
26 | { |
27 | int data, ret; |
28 | u8 shift = index & 2; |
e7638524 |
29 | __le32 ocp_data; |
c073f666 |
30 | |
31 | index &= ~3; |
32 | |
33 | ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), |
34 | RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, |
e7638524 |
35 | index, MCU_TYPE_PLA, &ocp_data, sizeof(ocp_data), |
36 | 500); |
c073f666 |
37 | if (ret < 0) |
38 | return ret; |
39 | |
e7638524 |
40 | data = __le32_to_cpu(ocp_data); |
c073f666 |
41 | data >>= (shift * 8); |
42 | data &= 0xffff; |
43 | |
44 | return data; |
45 | } |
46 | |
47 | static int pla_write_word(struct usb_device *udev, u16 index, u32 data) |
48 | { |
e7638524 |
49 | __le32 ocp_data; |
50 | u32 mask = 0xffff; |
c073f666 |
51 | u16 byen = BYTE_EN_WORD; |
52 | u8 shift = index & 2; |
53 | int ret; |
54 | |
55 | data &= mask; |
56 | |
57 | if (shift) { |
58 | byen <<= shift; |
59 | mask <<= (shift * 8); |
60 | data <<= (shift * 8); |
61 | index &= ~3; |
62 | } |
63 | |
64 | ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), |
65 | RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, |
e7638524 |
66 | index, MCU_TYPE_PLA, &ocp_data, sizeof(ocp_data), |
67 | 500); |
c073f666 |
68 | if (ret < 0) |
69 | return ret; |
70 | |
e7638524 |
71 | data |= __le32_to_cpu(ocp_data) & ~mask; |
72 | ocp_data = __cpu_to_le32(data); |
c073f666 |
73 | |
74 | ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), |
75 | RTL815x_REQ_SET_REGS, RTL815x_REQT_WRITE, |
e7638524 |
76 | index, MCU_TYPE_PLA | byen, &ocp_data, |
77 | sizeof(ocp_data), 500); |
c073f666 |
78 | |
79 | return ret; |
80 | } |
81 | |
82 | static int ocp_reg_read(struct usbnet *dev, u16 addr) |
83 | { |
84 | u16 ocp_base, ocp_index; |
85 | int ret; |
86 | |
87 | ocp_base = addr & 0xf000; |
88 | ret = pla_write_word(dev->udev, OCP_BASE, ocp_base); |
89 | if (ret < 0) |
90 | goto out; |
91 | |
92 | ocp_index = (addr & 0x0fff) | 0xb000; |
93 | ret = pla_read_word(dev->udev, ocp_index); |
94 | |
95 | out: |
96 | return ret; |
97 | } |
98 | |
99 | static int ocp_reg_write(struct usbnet *dev, u16 addr, u16 data) |
100 | { |
101 | u16 ocp_base, ocp_index; |
102 | int ret; |
103 | |
104 | ocp_base = addr & 0xf000; |
105 | ret = pla_write_word(dev->udev, OCP_BASE, ocp_base); |
106 | if (ret < 0) |
107 | goto out1; |
108 | |
109 | ocp_index = (addr & 0x0fff) | 0xb000; |
110 | ret = pla_write_word(dev->udev, ocp_index, data); |
111 | |
112 | out1: |
113 | return ret; |
114 | } |
115 | |
116 | static int r815x_mdio_read(struct net_device *netdev, int phy_id, int reg) |
117 | { |
118 | struct usbnet *dev = netdev_priv(netdev); |
119 | |
120 | if (phy_id != R815x_PHY_ID) |
121 | return -EINVAL; |
122 | |
123 | return ocp_reg_read(dev, BASE_MII + reg * 2); |
124 | } |
125 | |
126 | static |
127 | void r815x_mdio_write(struct net_device *netdev, int phy_id, int reg, int val) |
128 | { |
129 | struct usbnet *dev = netdev_priv(netdev); |
130 | |
131 | if (phy_id != R815x_PHY_ID) |
132 | return; |
133 | |
134 | ocp_reg_write(dev, BASE_MII + reg * 2, val); |
135 | } |
136 | |
137 | static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) |
138 | { |
139 | int status; |
140 | |
141 | status = usbnet_cdc_bind(dev, intf); |
142 | if (status < 0) |
143 | return status; |
144 | |
145 | dev->mii.dev = dev->net; |
146 | dev->mii.mdio_read = r815x_mdio_read; |
147 | dev->mii.mdio_write = r815x_mdio_write; |
148 | dev->mii.phy_id_mask = 0x3f; |
149 | dev->mii.reg_num_mask = 0x1f; |
150 | dev->mii.phy_id = R815x_PHY_ID; |
151 | dev->mii.supports_gmii = 1; |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static int r8152_bind(struct usbnet *dev, struct usb_interface *intf) |
157 | { |
158 | int status; |
159 | |
160 | status = usbnet_cdc_bind(dev, intf); |
161 | if (status < 0) |
162 | return status; |
163 | |
164 | dev->mii.dev = dev->net; |
165 | dev->mii.mdio_read = r815x_mdio_read; |
166 | dev->mii.mdio_write = r815x_mdio_write; |
167 | dev->mii.phy_id_mask = 0x3f; |
168 | dev->mii.reg_num_mask = 0x1f; |
169 | dev->mii.phy_id = R815x_PHY_ID; |
170 | dev->mii.supports_gmii = 0; |
171 | |
172 | return 0; |
173 | } |
174 | |
175 | static const struct driver_info r8152_info = { |
176 | .description = "RTL8152 ECM Device", |
177 | .flags = FLAG_ETHER | FLAG_POINTTOPOINT, |
178 | .bind = r8152_bind, |
179 | .unbind = usbnet_cdc_unbind, |
180 | .status = usbnet_cdc_status, |
181 | .manage_power = usbnet_manage_power, |
182 | }; |
183 | |
184 | static const struct driver_info r8153_info = { |
185 | .description = "RTL8153 ECM Device", |
186 | .flags = FLAG_ETHER | FLAG_POINTTOPOINT, |
187 | .bind = r8153_bind, |
188 | .unbind = usbnet_cdc_unbind, |
189 | .status = usbnet_cdc_status, |
190 | .manage_power = usbnet_manage_power, |
191 | }; |
192 | |
193 | static const struct usb_device_id products[] = { |
194 | { |
195 | USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM, |
196 | USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), |
197 | #if defined(CONFIG_USB_RTL8152) || defined(CONFIG_USB_RTL8152_MODULE) |
198 | .driver_info = 0, |
199 | #else |
200 | .driver_info = (unsigned long) &r8152_info, |
201 | #endif |
202 | }, |
203 | |
204 | { |
205 | USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM, |
206 | USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), |
207 | #if defined(CONFIG_USB_RTL8153) || defined(CONFIG_USB_RTL8153_MODULE) |
208 | .driver_info = 0, |
209 | #else |
210 | .driver_info = (unsigned long) &r8153_info, |
211 | #endif |
212 | }, |
213 | |
214 | { }, /* END */ |
215 | }; |
216 | MODULE_DEVICE_TABLE(usb, products); |
217 | |
218 | static struct usb_driver r815x_driver = { |
219 | .name = "r815x", |
220 | .id_table = products, |
221 | .probe = usbnet_probe, |
222 | .disconnect = usbnet_disconnect, |
223 | .suspend = usbnet_suspend, |
224 | .resume = usbnet_resume, |
225 | .reset_resume = usbnet_resume, |
226 | .supports_autosuspend = 1, |
227 | .disable_hub_initiated_lpm = 1, |
228 | }; |
229 | |
230 | module_usb_driver(r815x_driver); |
231 | |
232 | MODULE_AUTHOR("Hayes Wang"); |
233 | MODULE_DESCRIPTION("Realtek USB ECM device"); |
234 | MODULE_LICENSE("GPL"); |