1 /*
2 * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver.
3 * Copyright (c) 2008, 2011 S.Zharski <imker@gmx.li>
4 * Distributed under the terms of the MIT license.
5 *
6 * Heavily based on code of the
7 * Driver for USB Ethernet Control Model devices
8 * Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
9 * Distributed under the terms of the MIT license.
10 *
11 */
12
13
14 #include "MIIBus.h"
15
16 #include "ASIXVendorRequests.h"
17 #include "Driver.h"
18 #include "Settings.h"
19
20
21 #define MII_OUI(id1, id2) (((id1) << 6) | ((id2) >> 10))
22 #define MII_MODEL(id2) (((id2) & 0x03f0) >> 4)
23 #define MII_REV(id2) ((id2) & 0x000f)
24
25
MIIBus()26 MIIBus::MIIBus()
27 :
28 fStatus(B_NO_INIT),
29 fDevice(0),
30 fSelectedPHY(CurrentPHY)
31 {
32 for (size_t i = 0; i < PHYsCount; i++) {
33 fPHYs[i] = PHYNotInstalled;
34 }
35 }
36
37
38 status_t
Init(usb_device device)39 MIIBus::Init(usb_device device)
40 {
41 // reset to default state
42 fDevice = 0;
43 fSelectedPHY = CurrentPHY;
44 for (size_t i = 0; i < PHYsCount; i++) {
45 fPHYs[i] = PHYNotInstalled;
46 }
47
48 size_t actual_length = 0;
49 status_t result = gUSBModule->send_request(device,
50 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_PHYID, 0, 0,
51 sizeof(fPHYs), fPHYs, &actual_length);
52
53 if (result != B_OK) {
54 TRACE_ALWAYS("Request of the PHYIDs failed:%#010x\n", result);
55 return result;
56 }
57
58 if (sizeof(fPHYs) != actual_length) {
59 TRACE_ALWAYS("Mismatch of reading %d PHYIDs bytes instead of %d.\n",
60 actual_length, sizeof(fPHYs));
61 }
62
63 TRACE("PHYIDs are:%#02x:%#02x\n", fPHYs[0], fPHYs[1]);
64
65 // simply tactic - we use first available PHY
66 if (PHYType(PrimaryPHY) != PHYNotInstalled) {
67 fSelectedPHY = PrimaryPHY;
68 } else
69 if (PHYType(SecondaryPHY) != PHYNotInstalled) {
70 fSelectedPHY = SecondaryPHY;
71 }
72
73 TRACE("PHYs are configured: Selected:%#02x; Primary:%#02x; 2ndary:%#02x\n",
74 PHYID(CurrentPHY), PHYID(PrimaryPHY), PHYID(SecondaryPHY));
75 if (fSelectedPHY == CurrentPHY) {
76 TRACE_ALWAYS("No PHYs found!\n");
77 return B_ENTRY_NOT_FOUND;
78 }
79
80 fDevice = device;
81 fStatus = result;
82
83 return fStatus;
84 }
85
86
87 status_t
SetupPHY()88 MIIBus::SetupPHY()
89 {
90 uint16 control = 0;
91 status_t result = Read(MII_BMCR, &control);
92 if (result != B_OK) {
93 TRACE_ALWAYS("Error of reading control word:%#010x.\n", result);
94 return result;
95 }
96
97 TRACE("MII Control word is %#04x\n", control);
98
99 control &= ~BMCR_Isolate;
100 result = Write(MII_BMCR, control);
101 if (result != B_OK) {
102 TRACE_ALWAYS("Error of writing control word %#04x:%#010x.\n",
103 control, result);
104 }
105
106 result = Write(MII_BMCR, BMCR_Reset);
107 if (result != B_OK) {
108 TRACE_ALWAYS("Error of resetting PHY:%#010x.\n", result);
109 }
110
111 uint16 id01 = 0, id02 = 0;
112 result = Read(MII_PHYID0, &id01);
113 if (result != B_OK) {
114 TRACE_ALWAYS("Error of reading PHY ID1:%#010x.\n", result);
115 }
116
117 result = Read(MII_PHYID1, &id02);
118 if (result != B_OK) {
119 TRACE_ALWAYS("Error of reading PHY ID2:%#010x.\n", result);
120 }
121
122 TRACE("MII Info: OUI:%04x; Model:%04x; rev:%02x.\n",
123 MII_OUI(id01, id02), MII_MODEL(id02), MII_REV(id02));
124
125 // Dump();
126
127 return result;
128 }
129
130
131 status_t
InitCheck()132 MIIBus::InitCheck()
133 {
134 if (fSelectedPHY == CurrentPHY) {
135 return B_ENTRY_NOT_FOUND;
136 }
137
138 return fStatus;
139 }
140
141
142 uint8
PHYID(PHYIndex phyIndex)143 MIIBus::PHYID(PHYIndex phyIndex /*= CurrentPHY*/)
144 {
145 if (phyIndex == CurrentPHY) {
146 return (fSelectedPHY == CurrentPHY
147 ? 0 : fPHYs[fSelectedPHY]) & PHYIDMask;
148 }
149
150 return fPHYs[phyIndex] & PHYIDMask;
151 }
152
153
154 uint8
PHYType(PHYIndex phyIndex)155 MIIBus::PHYType(PHYIndex phyIndex /*= CurrentPHY*/)
156 {
157 if (phyIndex == CurrentPHY) {
158 return (fSelectedPHY == CurrentPHY
159 ? PHYNotInstalled : fPHYs[fSelectedPHY]) & PHYTypeMask;
160 }
161
162 return fPHYs[phyIndex] & PHYTypeMask;
163 }
164
165
166 status_t
Read(uint16 miiRegister,uint16 * value,PHYIndex phyIndex)167 MIIBus::Read(uint16 miiRegister, uint16 *value, PHYIndex phyIndex /*= CurrPHY*/)
168 {
169 status_t result = InitCheck();
170 if (B_OK != result) {
171 TRACE_ALWAYS("Error: MII is not ready:%#010x\n", result);
172 return result;
173 }
174
175 if (PHYType(phyIndex) == PHYNotInstalled) {
176 TRACE_ALWAYS("Error: Invalid PHY index:%#02x.\n", phyIndex);
177 return B_ENTRY_NOT_FOUND;
178 }
179
180 uint16 phyId = PHYID(phyIndex);
181
182 size_t actual_length = 0;
183 // switch to SW operation mode
184 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
185 | USB_REQTYPE_DEVICE_OUT, SW_MII_OP, 0, 0, 0, 0, &actual_length);
186
187 if (result != B_OK) {
188 TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result);
189 return result;
190 }
191
192 // read register value
193 status_t op_result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
194 | USB_REQTYPE_DEVICE_IN, READ_MII, phyId, miiRegister, sizeof(*value),
195 value, &actual_length);
196
197 if (op_result != B_OK) {
198 TRACE_ALWAYS("Error of reading MII reg.%d at PHY%d:%#010x.\n",
199 miiRegister, phyId, op_result);
200 }
201
202 if (sizeof(*value) != actual_length) {
203 TRACE_ALWAYS("Mismatch of reading MII reg.%d at PHY %d. "
204 "Read %d bytes instead of %d.\n", miiRegister, phyId,
205 actual_length, sizeof(*value));
206 }
207
208 // switch to HW operation mode
209 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
210 | USB_REQTYPE_DEVICE_OUT, HW_MII_OP, 0, 0, 0, 0, &actual_length);
211
212 if (result != B_OK) {
213 TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result);
214 }
215
216 return op_result;
217 }
218
219
220 status_t
Write(uint16 miiRegister,uint16 value,PHYIndex phyIndex)221 MIIBus::Write(uint16 miiRegister, uint16 value, PHYIndex phyIndex /*= CurrPHY*/)
222 {
223 size_t actual_length = 0;
224
225 status_t result = InitCheck();
226 if (B_OK != result) {
227 TRACE_ALWAYS("Error: MII is not ready:%#010x\n", result);
228 return result;
229 }
230
231 if (PHYType(phyIndex) == PHYNotInstalled) {
232 TRACE_ALWAYS("Error: Invalid PHY index:%#02x\n", phyIndex);
233 return B_ENTRY_NOT_FOUND;
234 }
235
236 uint16 phyId = PHYID(phyIndex);
237
238 // switch to SW operation mode
239 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
240 | USB_REQTYPE_DEVICE_OUT, SW_MII_OP, 0, 0, 0, 0, &actual_length);
241
242 if (result != B_OK) {
243 TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result);
244 return result;
245 }
246
247 // write register value
248 status_t op_result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
249 | USB_REQTYPE_DEVICE_OUT, WRITE_MII, phyId, miiRegister, sizeof(value),
250 &value, &actual_length);
251
252 if (op_result != B_OK) {
253 TRACE_ALWAYS("Error of writing MII reg.%d at PHY %d:%#010x.\n",
254 miiRegister, phyId, op_result);
255 }
256
257 if (sizeof(value) != actual_length) {
258 TRACE_ALWAYS("Mismatch of writing MII reg.%d at PHY %d."
259 "Write %d bytes instead of %d.\n", miiRegister, phyId,
260 actual_length, sizeof(value));
261 }
262
263 // switch to HW operation mode
264 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
265 | USB_REQTYPE_DEVICE_OUT, HW_MII_OP, 0, 0, 0, 0, &actual_length);
266
267 if (result != B_OK) {
268 TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result);
269 }
270
271 return op_result;
272 }
273
274
275 status_t
Status(uint16 * status,PHYIndex phyIndex)276 MIIBus::Status(uint16 *status, PHYIndex phyIndex /*= CurrentPHY*/)
277 {
278 return Read(MII_BMSR, status, phyIndex);
279 }
280
281
282 status_t
Dump()283 MIIBus::Dump()
284 {
285 status_t result = InitCheck();
286 if (B_OK != result) {
287 TRACE_ALWAYS("Error: MII is not ready:%#010x.\n", result);
288 return result;
289 }
290
291 if (PHYType(CurrentPHY) == PHYNotInstalled) {
292 TRACE_ALWAYS("Error: Current PHY index is invalid!\n");
293 return B_ENTRY_NOT_FOUND;
294 }
295
296 uint16 phyId = PHYID(CurrentPHY);
297
298 size_t actual_length = 0;
299 // switch to SW operation mode
300 result = gUSBModule->send_request(fDevice,
301 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
302 SW_MII_OP, 0, 0, 0, 0, &actual_length);
303
304 if (result != B_OK) {
305 TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result);
306 return result;
307 }
308
309 uint8 regs[] = { MII_BMCR, MII_BMSR, MII_PHYID0,
310 MII_PHYID1, MII_ANAR, MII_ANLPAR/*, MII_ANER*/};
311 uint16 value = 0;
312 for (size_t i = 0; i < sizeof(regs)/ sizeof(regs[0]); i++) {
313
314 // read register value
315 status_t op_result = gUSBModule->send_request(fDevice,
316 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_MII, phyId,
317 regs[i], sizeof(value), &value, &actual_length);
318
319 if (op_result != B_OK) {
320 TRACE_ALWAYS("Error of reading MII reg.%d at PHY%d:%#010x.\n",
321 regs[i], phyId, op_result);
322 }
323
324 if (sizeof(value) != actual_length) {
325 TRACE_ALWAYS("Mismatch of reading MII reg.%d at PHY%d."
326 " Read %d bytes instead of %d.\n", regs[i], phyId,
327 actual_length, sizeof(value));
328 }
329
330 TRACE_ALWAYS("MII reg: %d has %#04x\n", regs[i], value);
331 }
332
333 // switch to HW operation mode
334 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR
335 | USB_REQTYPE_DEVICE_OUT, HW_MII_OP, 0, 0, 0, 0, &actual_length);
336
337 if (result != B_OK) {
338 TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result);
339 }
340
341 return result;
342
343 }
344