xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_asix/MIIBus.cpp (revision b28ed9e04a771e5de38be68abd08148c0bbafc56)
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 
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
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
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
132 MIIBus::InitCheck()
133 {
134 	if (fSelectedPHY == CurrentPHY) {
135 		return B_ENTRY_NOT_FOUND;
136 	}
137 
138 	return fStatus;
139 }
140 
141 
142 uint8
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
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
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
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
276 MIIBus::Status(uint16 *status, PHYIndex phyIndex /*= CurrentPHY*/)
277 {
278 	return Read(MII_BMSR, status, phyIndex);
279 }
280 
281 
282 status_t
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