1/*
2 *	SiS 190/191 NIC Driver.
3 *	Copyright (c) 2009 S.Zharski <imker@gmx.li>
4 *	Distributed under the terms of the MIT license.
5 *
6 */
7
8
9#include "Device.h"
10
11#include <net/if_media.h>
12#include <lock.h>
13#include <stdio.h>
14
15#include "Driver.h"
16#include "Settings.h"
17#include "Registers.h"
18
19
20Device::Device(Device::Info &DeviceInfo, pci_info &PCIInfo)
21		:
22		fStatus(B_ERROR),
23		fPCIInfo(PCIInfo),
24		fInfo(DeviceInfo),
25		fIOBase(0),
26		fHWSpinlock(0),
27		fInterruptsNest(0),
28		fFrameSize(MaxFrameSize),
29		fMII(this),
30		fOpen(false),
31		fBlockFlag(0),
32		fLinkStateChangeSem(-1),
33		fHasConnection(false),
34		fTxDataRing(this, true),
35		fRxDataRing(this, false)
36{
37	memset((struct timer*)this, 0, sizeof(struct timer));
38
39	uint32 cmdRegister = gPCIModule->read_pci_config(PCIInfo.bus,
40			PCIInfo.device,	PCIInfo.function, PCI_command, 2);
41	TRACE_ALWAYS("cmdRegister:%#010x\n", cmdRegister);
42	cmdRegister |= PCI_command_io | PCI_command_memory | PCI_command_master;
43	gPCIModule->write_pci_config(PCIInfo.bus, PCIInfo.device,
44			PCIInfo.function, PCI_command, 2, cmdRegister);
45
46	fIOBase = PCIInfo.u.h0.base_registers[1];
47	TRACE_ALWAYS("fIOBase:%#010x\n", fIOBase);
48
49	fStatus = B_OK;
50}
51
52
53Device::~Device()
54{
55}
56
57
58status_t
59Device::Open(uint32 flags)
60{
61	TRACE("flags:%x\n", flags);
62	if (fOpen) {
63		TRACE_ALWAYS("An attempt to re-open device ignored.\n");
64		return B_BUSY;
65	}
66
67	status_t result = fMII.Init();
68	if (result != B_OK) {
69		TRACE_ALWAYS("MII initialization failed: %#010x.\n", result);
70		return result;
71	}
72
73	_Reset();
74
75	if ((fMII.LinkState().media & IFM_ACTIVE) == 0/*fNegotiationComplete*/) {
76		fMII.UpdateLinkState();
77	}
78
79	fMII.SetMedia();
80
81	WritePCI32(RxMACAddress, 0);
82	_InitRxFilter();
83
84	fRxDataRing.Open();
85	fTxDataRing.Open();
86
87	if (atomic_add(&fInterruptsNest, 1) == 0) {
88		install_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line,
89				InterruptHandler, this, 0);
90		TRACE("Interrupt handler installed at line %d.\n",
91				fPCIInfo.u.h0.interrupt_line);
92	}
93
94	_SetRxMode();
95
96	// enable al known interrupts
97	WritePCI32(IntMask, knownInterruptsMask);
98
99	// enable Rx and Tx
100	uint32 control = ReadPCI32(RxControl);
101	control |= RxControlEnable | RxControlPoll;
102	WritePCI32(RxControl, control);
103
104	control = ReadPCI32(TxControl);
105	control |= TxControlEnable /*| TxControlPoll*/;
106	WritePCI32(TxControl, control);
107
108	add_timer((timer*)this, _TimerHandler, 1000000LL, B_PERIODIC_TIMER);
109
110	//fNonBlocking = (flags & O_NONBLOCK) == O_NONBLOCK;
111	fOpen = true;
112	return B_OK;
113}
114
115
116status_t
117Device::Close()
118{
119	TRACE("closed!\n");
120
121	// disable interrupts
122	WritePCI32(IntMask, 0);
123	spin(2000);
124
125	// Stop Tx / Rx status machine
126	uint32 status = ReadPCI32(IntControl);
127	status |= 0x00008000;
128	WritePCI32(IntControl, status);
129	spin(50);
130	status &= ~0x00008000;
131	WritePCI32(IntControl, status);
132
133	if (atomic_add(&fInterruptsNest, -1) == 1) {
134		remove_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line,
135				InterruptHandler, this);
136		TRACE("Interrupt handler at line %d uninstalled.\n",
137				fPCIInfo.u.h0.interrupt_line);
138	}
139
140	fRxDataRing.Close();
141	fTxDataRing.Close();
142
143	cancel_timer((timer*)this);
144
145	TRACE("timer cancelled\n");
146
147	fOpen = false;
148
149	return B_OK;
150}
151
152
153status_t
154Device::Free()
155{
156	//	fRxDataRing.Free();
157	//	fTxDataRing.Free();
158
159	TRACE("freed\n");
160	return B_OK;
161}
162
163
164status_t
165Device::Read(uint8 *buffer, size_t *numBytes)
166{
167	return fRxDataRing.Read(buffer, numBytes);
168}
169
170
171status_t
172Device::Write(const uint8 *buffer, size_t *numBytes)
173{
174	if ((fMII.LinkState().media & IFM_ACTIVE) == 0) {
175		TRACE_ALWAYS("Write failed. link is inactive!\n");
176		return B_OK; // return OK because of well-known DHCP "moustreap"!
177	}
178
179	return fTxDataRing.Write(buffer, numBytes);
180}
181
182
183status_t
184Device::Control(uint32 op, void *buffer, size_t length)
185{
186	switch (op) {
187		case ETHER_INIT:
188			TRACE("ETHER_INIT\n");
189			return B_OK;
190
191		case ETHER_GETADDR:
192			memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
193			TRACE("ETHER_GETADDR %#02x:%#02x:%#02x:%#02x:%#02x:%#02x\n",
194					fMACAddress.ebyte[0], fMACAddress.ebyte[1],
195					fMACAddress.ebyte[2], fMACAddress.ebyte[3],
196					fMACAddress.ebyte[4], fMACAddress.ebyte[5]);
197			return B_OK;
198
199		case ETHER_GETFRAMESIZE:
200			*(uint32 *)buffer = fFrameSize;
201			TRACE("ETHER_ETHER_GETFRAMESIZE:%d\n",fFrameSize);
202			return B_OK;
203
204		case ETHER_NONBLOCK:
205			TRACE("ETHER_NONBLOCK\n");
206			fBlockFlag = *((uint32*)buffer) ? B_TIMEOUT : 0;
207			return B_OK;
208
209		case ETHER_SETPROMISC:
210			return _SetRxMode((uint8*)buffer);
211
212		case ETHER_ADDMULTI:
213			return _ModifyMulticastTable(true, (ether_address_t*)buffer);
214
215		case ETHER_REMMULTI:
216			return _ModifyMulticastTable(false, (ether_address_t*)buffer);
217
218		case ETHER_SET_LINK_STATE_SEM:
219			fLinkStateChangeSem = *(sem_id *)buffer;
220			TRACE_ALWAYS("ETHER_SET_LINK_STATE_SEM\n");
221			return B_OK;
222
223		case ETHER_GET_LINK_STATE:
224			return GetLinkState((ether_link_state *)buffer);
225
226		default:
227			TRACE_ALWAYS("Unhandled IOCTL catched: %#010x\n", op);
228	}
229
230	return B_DEV_INVALID_IOCTL;
231}
232
233
234status_t
235Device::SetupDevice()
236{
237	ether_address address;
238	status_t result = ReadMACAddress(address);
239	if (result != B_OK) {
240		TRACE_ALWAYS("Error of reading MAC address:%#010x\n", result);
241		return result;
242	}
243
244	TRACE("MAC address is:%02x:%02x:%02x:%02x:%02x:%02x\n",
245			address.ebyte[0], address.ebyte[1], address.ebyte[2],
246			address.ebyte[3], address.ebyte[4], address.ebyte[5]);
247
248	fMACAddress = address;
249
250	uint16 info = _ReadEEPROM(EEPROMInfo);
251	fMII.SetRGMII((info & 0x0080) != 0);
252
253	TRACE("RGMII is '%s'. EEPROM info word:%#06x.\n",
254			fMII.HasRGMII() ? "on" : "off", info);
255
256	fMII.SetGigagbitCapable(fInfo.Id() == SiS191);
257
258	return B_OK;
259}
260
261
262void
263Device::TeardownDevice()
264{
265
266}
267
268
269uint8
270Device::ReadPCI8(int offset)
271{
272	return gPCIModule->read_io_8(fIOBase + offset);
273}
274
275
276uint16
277Device::ReadPCI16(int offset)
278{
279	return gPCIModule->read_io_16(fIOBase + offset);
280}
281
282
283uint32
284Device::ReadPCI32(int offset)
285{
286	return gPCIModule->read_io_32(fIOBase + offset);
287}
288
289
290void
291Device::WritePCI8(int offset, uint8 value)
292{
293	gPCIModule->write_io_8(fIOBase + offset, value);
294}
295
296
297void
298Device::WritePCI16(int offset, uint16 value)
299{
300	gPCIModule->write_io_16(fIOBase + offset, value);
301}
302
303
304void
305Device::WritePCI32(int offset, uint32 value)
306{
307	gPCIModule->write_io_32(fIOBase + offset, value);
308}
309
310/*
311   cpu_status
312   Device::Lock()
313   {
314   cpu_status st = disable_interrupts();
315   acquire_spinlock(&fHWSpinlock);
316   return st;
317   }
318
319
320   void
321   Device::Unlock(cpu_status st)
322   {
323   release_spinlock(&fHWSpinlock);
324   restore_interrupts(st);
325   }
326   */
327
328int32
329Device::InterruptHandler(void *InterruptParam)
330{
331	Device *device = (Device*)InterruptParam;
332	if(device == 0) {
333		TRACE_ALWAYS("Invalid parameter in the interrupt handler.\n");
334		return B_HANDLED_INTERRUPT;
335	}
336
337	int32 result = B_UNHANDLED_INTERRUPT;
338
339	acquire_spinlock(&device->fHWSpinlock);
340
341	// disable interrupts...
342	device->WritePCI32(IntMask, 0);
343
344	//int maxWorks = 40;
345
346	//do {
347	uint32 status = device->ReadPCI32(IntSource);
348
349#if STATISTICS
350	device->fStatistics.PutStatus(status);
351#endif
352	device->WritePCI32(IntSource, status);
353
354	if ((status & knownInterruptsMask) != 0) {
355		//break;
356		//}
357
358		// XXX: ????
359		result = B_HANDLED_INTERRUPT;
360
361	if ((status & (/*INT_TXIDLE |*/ INT_TXDONE)) != 0 ) {
362		result = device->fTxDataRing.InterruptHandler();
363	}
364
365	if ((status & (/*INT_RXIDLE |*/ INT_RXDONE)) != 0 ) {
366		result = device->fRxDataRing.InterruptHandler();
367	}
368
369	/*if ((status & (INT_LINK)) != 0 ) {
370	//if (!device->fMII.isLinkUp()) {
371	device->fTxDataRing.CleanUp();
372	//}
373	}*/
374	}
375
376	//} while (--maxWorks > 0);
377
378	// enable interrupts...
379	device->WritePCI32(IntMask, knownInterruptsMask);
380
381	release_spinlock(&device->fHWSpinlock);
382
383	return result;
384}
385
386
387status_t
388Device::GetLinkState(ether_link_state *linkState)
389{
390	status_t result = user_memcpy(linkState, &fMII.LinkState(),
391			sizeof(ether_link_state));
392
393#if STATISTICS
394	fStatistics.Trace();
395	fRxDataRing.Trace();
396	fTxDataRing.Trace();
397	uint32 rxControl = ReadPCI32(RxControl);
398	uint32 txControl = ReadPCI32(TxControl);
399	TRACE_ALWAYS("RxControl:%#010x;TxControl:%#010x\n", rxControl, txControl);
400#endif
401
402	TRACE_FLOW("Medium state: %s, %lld MBit/s, %s duplex.\n",
403			(linkState->media & IFM_ACTIVE) ? "active" : "inactive",
404			linkState->speed / 1000,
405			(linkState->media & IFM_FULL_DUPLEX) ? "full" : "half");
406
407	return result;
408}
409
410
411status_t
412Device::_SetRxMode(uint8* setPromiscuousOn)
413{
414	uint16 rxMode = ReadPCI16(RxMACControl);
415
416	// clean the Rx MAC Control register
417	WritePCI16(RxMACControl, rxMode & ~RXM_Mask);
418
419	rxMode &= RXM_Mask;
420
421	if (setPromiscuousOn != NULL) {
422		if (*setPromiscuousOn != 0)
423			rxMode |= RXM_AllPhysical;
424		else
425			rxMode &= ~RXM_AllPhysical;
426	}
427
428	uint32  multicastFilter[2] = { 0 };
429	if ((rxMode & RXM_AllPhysical) == 0	&& fMulticastHashes.Count() <= 128)	{
430
431		for (int32 i = 0; i < fMulticastHashes.Count(); i++) {
432			uint32 hash = fMulticastHashes[i];
433			multicastFilter[hash >> 31] |= 1 << ((hash >> 26) & 0x1f);
434		}
435
436	} else
437		multicastFilter[0] = multicastFilter[1] = 0xffffffff;
438
439	rxMode |= RXM_Broadcast | RXM_Multicast | RXM_Physical;
440
441	// set multicast filters
442	WritePCI32(RxHashTable, multicastFilter[0]);
443	WritePCI32(RxHashTable + 4, multicastFilter[1]);
444
445	// update rx mode
446	WritePCI16(RxMACControl, ReadPCI16(RxMACControl) | rxMode);
447
448	return B_OK;
449}
450
451
452int32
453Device::_TimerHandler(struct timer* timer)
454{
455	Device* device = (Device*)timer;
456
457	bool linkChanged = false;
458	int32 result = device->fMII.TimerHandler(&linkChanged);
459
460	if (linkChanged) {
461		if (device->fMII.IsLinkUp()) {
462			device->fTxDataRing.CleanUp();
463			//device->WritePCI32(IntControl, 0x8000);
464			//device->ReadPCI32(IntControl);
465			//spin(100);
466			//device->WritePCI32(IntControl, 0x0);
467		}
468	}
469
470	if (linkChanged && device->fLinkStateChangeSem > B_OK) {
471		release_sem_etc(device->fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
472	}
473
474	return result;
475}
476
477
478status_t
479Device::_Reset()
480{
481	// disable interrupts
482	WritePCI32(IntMask, 0);
483	WritePCI32(IntSource, 0xffffffff);
484
485	// reset Rx & Tx
486	WritePCI32(TxControl, 0x00001c00);
487	WritePCI32(RxControl, 0x001e1c00);
488
489	WritePCI32(IntControl, 0x8000);
490	ReadPCI32(IntControl);
491	spin(100);
492	WritePCI32(IntControl, 0x0);
493
494	WritePCI32(IntMask, 0);
495	WritePCI32(IntSource, 0xffffffff);
496
497	// initial values for all MAC registers
498	WritePCI32(TxBase,			0x0);
499	WritePCI32(TxReserved,		0x0);
500	WritePCI32(RxBase,			0x0);
501	WritePCI32(RxReserved,		0x0);
502
503	WritePCI32(PowControl,		0xffc00000);
504	WritePCI32(Reserved0,		0x0);
505
506	WritePCI32(StationControl,	fMII.HasRGMII() ? 0x04008001 : 0x04000001);
507	WritePCI32(GIoCR,			0x0);
508	WritePCI32(GIoControl,		0x0);
509
510	WritePCI32(TxMACControl,	0x00002364);
511	WritePCI32(TxLimit,			0x0000000f);
512
513	WritePCI32(RGDelay,			0x0);
514	WritePCI32(Reserved1,		0x0);
515	WritePCI32(RxMACControl,	0x00000252);
516
517	WritePCI32(RxHashTable,		0x0);
518	WritePCI32(RxHashTable + 4,	0x0);
519
520	WritePCI32(RxWOLControl,	0x80ff0000);
521	WritePCI32(RxWOLData,		0x80ff0000);
522	WritePCI32(RxMPSControl,	0x0);
523	WritePCI32(Reserved2,		0x0);
524
525	return B_OK;
526}
527
528
529void
530Device::_InitRxFilter()
531{
532	// store filter value
533	uint16 filter = ReadPCI16(RxMACControl);
534
535	// disable disable packet filtering before address is set
536	WritePCI32(RxMACControl, (filter & ~RXM_Mask));
537
538	for (size_t i = 0; i < _countof(fMACAddress.ebyte); i++) {
539		WritePCI8(RxMACAddress + i, fMACAddress.ebyte[i]);
540	}
541
542	// clear promiscuous bit on initialization
543	filter &= ~RXM_AllPhysical;
544
545	// enable packet filtering
546	WritePCI16(RxMACControl, filter);
547}
548
549
550uint16
551Device::_ReadEEPROM(uint32 address)
552{
553	if (address > EIOffset) {
554		TRACE_ALWAYS("EEPROM address %#08x is invalid.\n", address);
555		return EIInvalid;
556	}
557
558	WritePCI32(EEPROMInterface, EIReq | EIOpRead | (address << EIOffsetShift));
559
560	spin(500); // 500 ms?
561
562	for (size_t i = 0; i < 1000; i++) {
563		uint32 data = ReadPCI32(EEPROMInterface);
564		if ((data & EIReq) == 0) {
565			return (data & EIData) >> EIDataShift;
566		}
567		spin(100); // 100 ms?
568	}
569
570	TRACE_ALWAYS("timeout reading EEPROM.\n");
571
572	return EIInvalid;
573}
574
575
576status_t
577Device::ReadMACAddress(ether_address_t& address)
578{
579	uint16 signature = _ReadEEPROM(EEPROMSignature);
580	TRACE("EEPROM Signature: %#06x\n", signature);
581
582	if (signature != 0x0000 && signature != EIInvalid) {
583		for (size_t i = 0; i < _countof(address.ebyte) / 2; i++) {
584			uint16 addr = _ReadEEPROM(EEPROMAddress + i);
585			address.ebyte[i * 2 + 0] = (uint8)addr;
586			address.ebyte[i * 2 + 1] = (uint8)(addr >> 8);
587		}
588
589		return B_OK;
590	}
591
592	// SiS96x can use APC CMOS RAM to store MAC address,
593	// this is accessed through ISA bridge.
594	uint32 register73 = gPCIModule->read_pci_config(fPCIInfo.bus,
595			fPCIInfo.device, fPCIInfo.function, 0x73, 1);
596	TRACE_ALWAYS("Config register x73:%#010x\n", register73);
597
598	if ((register73 & 0x00000001) == 0)
599		return B_ERROR;
600
601	// look for PCI-ISA bridge
602	uint16 ids[] = { 0x0965, 0x0966, 0x0968 };
603
604	pci_info pciInfo = {0};
605	for (long i = 0; B_OK == (*gPCIModule->get_nth_pci_info)(i, &pciInfo); i++) {
606		if (pciInfo.vendor_id != 0x1039)
607			continue;
608
609		for (size_t idx = 0; idx < _countof(ids); idx++) {
610			if (pciInfo.device_id == ids[idx]) {
611
612				// enable ports 0x78 0x79 to access APC registers
613				uint32 reg = gPCIModule->read_pci_config(pciInfo.bus,
614					pciInfo.device, pciInfo.function, 0x48, 1);
615				reg &= ~0x02;
616				gPCIModule->write_pci_config(pciInfo.bus,
617					pciInfo.device, pciInfo.function, 0x48, 1, reg);
618				snooze(50);
619				reg = gPCIModule->read_pci_config(pciInfo.bus,
620					pciInfo.device, pciInfo.function, 0x48, 1);
621
622				// read factory MAC address
623				for (size_t i = 0; i < _countof(address.ebyte); i++) {
624					gPCIModule->write_io_8(0x78, 0x09 + i);
625					address.ebyte[i] = gPCIModule->read_io_8(0x79);
626				}
627
628				// check MII/RGMII
629				gPCIModule->write_io_8(0x78, 0x12);
630				uint8 u8 = gPCIModule->read_io_8(0x79);
631			// TODO: set RGMII in fMII correctly!
632			//	bool bRGMII = (u8 & 0x80) != 0;
633				TRACE_ALWAYS("RGMII: %#04x\n", u8);
634
635				// close access to APC registers
636				gPCIModule->write_pci_config(pciInfo.bus,
637					pciInfo.device, pciInfo.function, 0x48, 1, reg);
638
639				return B_OK;
640			}
641		}
642	}
643
644	TRACE_ALWAYS("ISA bridge was not found.\n");
645	return B_ERROR;
646}
647
648
649uint32
650Device::_EthernetCRC32(const uint8* buffer, size_t length)
651{
652	uint32 result = 0xffffffff;
653	for (size_t i = 0; i < length; i++) {
654		uint8 data = *buffer++;
655		for (int bit = 0; bit < 8; bit++, data >>= 1) {
656			uint32 carry = ((result & 0x80000000) ? 1 : 0) ^ (data & 0x01);
657			result <<= 1;
658			if (carry != 0)
659				result = (result ^ 0x04c11db6) | carry;
660		}
661	}
662	return result;
663}
664
665
666status_t
667Device::_ModifyMulticastTable(bool join, ether_address_t* group)
668{
669	char groupName[6 * 3 + 1] = { 0 };
670	sprintf(groupName, "%02x:%02x:%02x:%02x:%02x:%02x",
671		group->ebyte[0], group->ebyte[1], group->ebyte[2],
672		group->ebyte[3], group->ebyte[4], group->ebyte[5]);
673	TRACE("%s multicast group %s\n", join ? "Joining" : "Leaving", groupName);
674
675	uint32 hash = _EthernetCRC32(group->ebyte, 6);
676	bool isInTable = fMulticastHashes.Find(hash) != fMulticastHashes.End();
677
678	if (isInTable && join)
679		return B_OK; // already listed - nothing to do
680
681	if (!isInTable && !join) {
682		TRACE_ALWAYS("Cannot leave unlisted multicast group %s!\n", groupName);
683		return B_ERROR;
684	}
685
686	if (join)
687		fMulticastHashes.PushBack(hash);
688	else
689		fMulticastHashes.Remove(hash);
690
691	return _SetRxMode();
692}
693
694
695void
696Device::DumpRegisters()
697{
698	struct RegisterEntry {
699		uint32 Base;
700		const char* Name;
701		bool writeBack;
702	} RegisterEntries[] = {
703		{ TxControl,		"TxControl",	false },
704		{ TxBase,			"TxBase\t",		false },
705		{ TxStatus,			"TxStatus",		false },
706		{ TxReserved,		"TxReserved",	false },
707		{ RxControl,		"RxControl",	false },
708		{ RxBase,			"RxBase\t",		false },
709		{ RxStatus,			"RxStatus",		false },
710		{ RxReserved,		"RxReserved",	false },
711		{ IntSource,		"IntSource",	true },
712		{ IntMask,			"IntMask",		false },
713		{ IntControl,		"IntControl",	false },
714		{ IntTimer,			"IntTimer",		false },
715		{ PowControl,		"PowControl",	false },
716		{ Reserved0,		"Reserved0",	false },
717		{ EEPROMControl,	"EEPROMCntl",	false },
718		{ EEPROMInterface,	"EEPROMIface",	false },
719		{ StationControl,	"StationCntl",	false },
720		{ SMInterface,		"SMInterface",	false },
721		{ GIoCR,			"GIoCR\t",		false },
722		{ GIoControl,		"GIoControl",	false },
723		{ TxMACControl,		"TxMACCntl",	false },
724		{ TxLimit,			"TxLimit",		false },
725		{ RGDelay,			"RGDelay",		false },
726		{ Reserved1,		"Reserved1",	false },
727		{ RxMACControl,		"RxMACCntlEtc",	false },
728		{ RxMACAddress + 2,	"RxMACAddr2",	false },
729		{ RxHashTable,		"RxHashTable1",	false },
730		{ RxHashTable + 4,	"RxHashTable2",	false },
731		{ RxWOLControl,		"RxWOLControl",	false },
732		{ RxWOLData,		"RxWOLData",	false },
733		{ RxMPSControl,		"RxMPSControl",	false },
734		{ Reserved2,		"Reserved2",	false }
735	};
736
737	for (size_t i = 0; i < _countof(RegisterEntries); i++) {
738		uint32 registerContents = ReadPCI32(RegisterEntries[i].Base);
739		kprintf("%s:\t%08" B_PRIx32 "\n", RegisterEntries[i].Name,
740			registerContents);
741		if (RegisterEntries[i].writeBack) {
742			WritePCI32(RegisterEntries[i].Base, registerContents);
743		}
744	}
745}
746
747