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
26MIIBus::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
38status_t
39MIIBus::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
87status_t
88MIIBus::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
131status_t
132MIIBus::InitCheck()
133{
134	if (fSelectedPHY == CurrentPHY) {
135		return B_ENTRY_NOT_FOUND;
136	}
137
138	return fStatus;
139}
140
141
142uint8
143MIIBus::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
154uint8
155MIIBus::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
166status_t
167MIIBus::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
220status_t
221MIIBus::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
275status_t
276MIIBus::Status(uint16 *status, PHYIndex phyIndex /*= CurrentPHY*/)
277{
278	return Read(MII_BMSR, status, phyIndex);
279}
280
281
282status_t
283MIIBus::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