1/* Copyright (c) 2003-2011
2 * Stefano Ceccherini <stefano.ceccherini@gmail.com>. All rights reserved.
3 * This file is released under the MIT license
4 */
5#include "device.h"
6#include "driver.h"
7#include "debug.h"
8#include "ether_driver.h"
9#include "interface.h"
10#include "wb840.h"
11
12#include <ByteOrder.h>
13#include <KernelExport.h>
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18
19#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
20
21// MII chip info table
22#define PHY_ID0_DAVICOM_DM9101	0x0181
23#define PHY_ID1_DAVICOM_DM9101	0xb800
24#define	MII_HOME	0x0001
25#define MII_LAN		0x0002
26
27struct mii_chip_info
28{
29	const char* name;
30	uint16 id0;
31	uint16 id1;
32	uint8  types;
33};
34
35
36const static struct mii_chip_info
37gMIIChips[] = {
38	{"DAVICOM_DM9101", PHY_ID0_DAVICOM_DM9101, PHY_ID1_DAVICOM_DM9101, MII_LAN},
39	{NULL, 0, 0, 0}
40};
41
42
43static int
44mii_readstatus(wb_device* device)
45{
46	int i = 0;
47	int status;
48
49	// status bit has to be retrieved 2 times
50	while (i++ < 2)
51		status = wb_miibus_readreg(device, device->phy, MII_STATUS);
52
53	return status;
54}
55
56
57static phys_addr_t
58physicalAddress(volatile void* addr, uint32 length)
59{
60	physical_entry table;
61
62	get_memory_map((void*)addr, length, &table, 1);
63
64	return table.address;
65}
66
67
68// Prepares a RX descriptor to be used by the chip
69void
70wb_put_rx_descriptor(volatile wb_desc* descriptor)
71{
72	descriptor->wb_status = WB_RXSTAT_OWN;
73	descriptor->wb_ctl |= WB_MAX_FRAMELEN | WB_RXCTL_RLINK;
74}
75
76
77void
78wb_enable_interrupts(wb_device* device)
79{
80	write32(device->reg_base + WB_IMR, WB_INTRS);
81	write32(device->reg_base + WB_ISR, 0xFFFFFFFF);
82}
83
84
85void
86wb_disable_interrupts(wb_device* device)
87{
88	write32(device->reg_base + WB_IMR, 0L);
89	write32(device->reg_base + WB_ISR, 0L);
90}
91
92
93static void
94wb_selectPHY(wb_device* device)
95{
96	uint16 status;
97
98	// ToDo: need to be changed, select PHY in relation to the link mode
99	device->currentPHY = device->firstPHY;
100	device->phy = device->currentPHY->address;
101	status = wb_miibus_readreg(device, device->phy, MII_CONTROL);
102	status &= ~MII_CONTROL_ISOLATE;
103
104	wb_miibus_writereg(device, device->phy, MII_CONTROL, status);
105
106	wb_read_mode(device);
107}
108
109
110status_t
111wb_initPHYs(wb_device* device)
112{
113	uint16 phy;
114	// search for total of 32 possible MII PHY addresses
115	for (phy = 0; phy < 32; phy++) {
116		struct mii_phy* mii;
117		uint16 status;
118		int i = 0;
119
120		status = wb_miibus_readreg(device, phy, MII_STATUS);
121		status = wb_miibus_readreg(device, phy, MII_STATUS);
122
123		if (status == 0xffff || status == 0x0000)
124			// this MII is not accessable
125			continue;
126
127		mii = (struct mii_phy*)calloc(1, sizeof(struct mii_phy));
128		if (mii == NULL)
129			return B_NO_MEMORY;
130
131		mii->address = phy;
132		mii->id0 = wb_miibus_readreg(device, phy, MII_PHY_ID0);
133		mii->id1 = wb_miibus_readreg(device, phy, MII_PHY_ID1);
134		mii->types = MII_HOME;
135		mii->next = device->firstPHY;
136		device->firstPHY = mii;
137
138		while (gMIIChips[i].name != NULL) {
139			if (gMIIChips[i].id0 == mii->id0
140				&& gMIIChips[i].id1 == (mii->id1 & 0xfff0)) {
141				dprintf("Found MII PHY: %s\n", gMIIChips[i].name);
142				mii->types = gMIIChips[i].types;
143				break;
144			}
145			i++;
146		}
147		if (gMIIChips[i].name == NULL) {
148			dprintf("Unknown MII PHY transceiver: id = (%x, %x).\n",
149				mii->id0, mii->id1);
150		}
151	}
152
153	if (device->firstPHY == NULL) {
154		dprintf("No MII PHY transceiver found!\n");
155		return B_ENTRY_NOT_FOUND;
156	}
157
158	wb_selectPHY(device);
159	device->link = mii_readstatus(device) & MII_STATUS_LINK;
160
161	return B_OK;
162}
163
164
165void
166wb_init(wb_device* device)
167{
168	LOG((DEVICE_NAME": init()\n"));
169
170	wb_reset(device);
171
172	device->wb_txthresh = WB_TXTHRESH_INIT;
173
174	switch(device->wb_cachesize) {
175		case 32:
176			WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_32LONG);
177			break;
178		case 16:
179			WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_16LONG);
180			break;
181		case 8:
182			WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_8LONG);
183			break;
184		case 0:
185		default:
186			WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_NONE);
187			break;
188	}
189
190	write32(device->reg_base + WB_BUSCTL,
191		WB_BUSCTL_MUSTBEONE | WB_BUSCTL_ARBITRATION);
192	WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BURSTLEN_16LONG);
193
194	write32(device->reg_base + WB_BUSCTL_SKIPLEN, WB_SKIPLEN_4LONG);
195
196	// Disable early TX/RX interrupt, as we can't take advantage
197	// from them, at least for now.
198	WB_CLRBIT(device->reg_base + WB_NETCFG,
199		(WB_NETCFG_TX_EARLY_ON | WB_NETCFG_RX_EARLY_ON));
200
201	wb_set_rx_filter(device);
202}
203
204
205void
206wb_reset(wb_device *device)
207{
208	int i = 0;
209
210	LOG((DEVICE_NAME": reset()\n"));
211
212	write32(device->reg_base + WB_NETCFG, 0L);
213	write32(device->reg_base + WB_BUSCTL, 0L);
214	write32(device->reg_base + WB_TXADDR, 0L);
215	write32(device->reg_base + WB_RXADDR, 0L);
216
217	WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
218	WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
219
220	for (i = 0; i < WB_TIMEOUT; i++) {
221		if (!(read32(device->reg_base + WB_BUSCTL) & WB_BUSCTL_RESET))
222			break;
223	}
224
225	if (i == WB_TIMEOUT)
226		LOG((DEVICE_NAME": reset hasn't completed!!!"));
227
228	/* Wait a bit while the chip reorders his toughts */
229	snooze(1000);
230}
231
232
233status_t
234wb_stop(wb_device* device)
235{
236	uint32 cfgAddress = (uint32)device->reg_base + WB_NETCFG;
237	int32 i = 0;
238
239	if (read32(cfgAddress) & (WB_NETCFG_TX_ON | WB_NETCFG_RX_ON)) {
240		WB_CLRBIT(cfgAddress, (WB_NETCFG_TX_ON | WB_NETCFG_RX_ON));
241
242		for (i = 0; i < WB_TIMEOUT; i++) {
243			if ((read32(device->reg_base + WB_ISR) & WB_ISR_TX_IDLE) &&
244				(read32(device->reg_base + WB_ISR) & WB_ISR_RX_IDLE))
245				break;
246		}
247	}
248
249	if (i < WB_TIMEOUT)
250		return B_OK;
251
252	return B_ERROR;
253}
254
255
256static void
257wb_updateLink(wb_device* device)
258{
259	if (!device->autoNegotiationComplete) {
260		int32 mode = wb_read_mode(device);
261		if (mode)
262			wb_set_mode(device, mode);
263
264		return;
265	}
266
267	if (device->link) {
268		uint16 status = mii_readstatus(device);
269
270		// Check if link lost
271		if ((status & MII_STATUS_LINK) == 0)
272			device->link = false;
273	} else {
274		uint16 status;
275		wb_selectPHY(device);
276
277		// Check if we have a new link
278		status = mii_readstatus(device);
279		if (status & MII_STATUS_LINK)
280			device->link = true;
281	}
282}
283
284
285int32
286wb_tick(timer* arg)
287{
288	wb_device* device = (wb_device*)arg;
289
290	wb_updateLink(device);
291
292	return B_HANDLED_INTERRUPT;
293}
294
295
296/*
297 * Program the rx filter.
298 */
299void
300wb_set_rx_filter(wb_device* device)
301{
302	// TODO: Basically we just config the filter to accept broadcasts
303	// packets. We'll need also to configure it to multicast.
304	WB_SETBIT(device->reg_base + WB_NETCFG, WB_NETCFG_RX_BROAD);
305}
306
307
308/***************** Interrupt handling ******************************/
309static status_t
310wb_rxok(wb_device* device)
311{
312	uint32 releaseRxSem = 0;
313	int16 limit;
314
315	acquire_spinlock(&device->rxSpinlock);
316
317	for (limit = device->rxFree; limit > 0; limit--) {
318		if (device->rxDescriptor[device->rxInterruptIndex].wb_status
319			& WB_RXSTAT_OWN) {
320			break;
321		}
322
323		releaseRxSem++;
324		device->rxInterruptIndex = (device->rxInterruptIndex + 1)
325			& WB_RX_CNT_MASK;
326		device->rxFree--;
327	}
328
329	// Re-enable receive queue
330	write32(device->reg_base + WB_RXSTART, 0xFFFFFFFF);
331
332	release_spinlock(&device->rxSpinlock);
333
334	if (releaseRxSem > 0) {
335		release_sem_etc(device->rxSem, releaseRxSem, B_DO_NOT_RESCHEDULE);
336		return B_INVOKE_SCHEDULER;
337	}
338
339	return B_HANDLED_INTERRUPT;
340}
341
342
343static status_t
344wb_tx_nobuf(wb_device* info)
345{
346	int16 releaseTxSem = 0;
347	int16 limit;
348	status_t status;
349
350	acquire_spinlock(&info->txSpinlock);
351
352	for (limit = info->txSent; limit > 0; limit--) {
353		status = info->txDescriptor[info->txInterruptIndex].wb_status;
354
355		LOG(("wb_tx_nobuf, status: %lx\n", status));
356		if (status & WB_TXSTAT_TXERR) {
357			LOG(("TX_STAT_ERR\n"));
358			break;
359		} else if (status & WB_UNSENT) {
360			LOG(("TX_STAT_UNSENT\n"));
361			break;
362		} else if (status & WB_TXSTAT_OWN) {
363			LOG((DEVICE_NAME": Device still owns the descriptor\n"));
364			break;
365		} else
366			info->txDescriptor[info->txInterruptIndex].wb_status = 0;
367
368		releaseTxSem++;	// this many buffers are free
369		info->txInterruptIndex = (info->txInterruptIndex + 1) & WB_TX_CNT_MASK;
370		info->txSent--;
371
372		if (info->txSent < 0 || info->txSent > WB_TX_LIST_CNT)
373			dprintf("ERROR interrupt: txSent = %d\n", info->txSent);
374	}
375
376	release_spinlock(&info->txSpinlock);
377
378	if (releaseTxSem) {
379		release_sem_etc(info->txSem, releaseTxSem, B_DO_NOT_RESCHEDULE);
380		return B_INVOKE_SCHEDULER;
381	}
382
383	return B_HANDLED_INTERRUPT;
384}
385
386
387int32
388wb_interrupt(void* arg)
389{
390	wb_device* device = (wb_device*)arg;
391	int32 retval = B_UNHANDLED_INTERRUPT;
392	uint32 status;
393
394	// TODO: Handle other interrupts
395
396	acquire_spinlock(&device->intLock);
397
398	status = read32(device->reg_base + WB_ISR);
399
400	// Did this card request the interrupt ?
401	if (status & WB_INTRS) {
402		// Clean all the interrupts bits
403		if (status)
404			write32(device->reg_base + WB_ISR, status);
405
406		if (status & WB_ISR_ABNORMAL)
407			LOG((DEVICE_NAME": *** Abnormal Interrupt received ***\n"));
408		else
409			LOG((DEVICE_NAME": interrupt received: \n"));
410
411		if (status & WB_ISR_RX_EARLY) {
412			LOG(("WB_ISR_RX_EARLY\n"));
413		}
414
415		if (status & WB_ISR_RX_NOBUF) {
416			LOG(("WB_ISR_RX_NOBUF\n"));
417			// Something is screwed
418		}
419
420		if (status & WB_ISR_RX_ERR) {
421			LOG(("WB_ISR_RX_ERR\n"));
422			// TODO: Do something useful
423		}
424
425		if (status & WB_ISR_RX_OK) {
426			LOG(("WB_ISR_RX_OK\n"));
427			retval = wb_rxok(device);
428		}
429
430		if (status & WB_ISR_RX_IDLE) {
431			LOG(("WB_ISR_RX_IDLE\n"));
432			// ???
433		}
434
435		if (status & WB_ISR_TX_EARLY) {
436			LOG(("WB_ISR_TX_EARLY\n"));
437
438		}
439
440		if (status & WB_ISR_TX_NOBUF) {
441			LOG(("WB_ISR_TX_NOBUF\n"));
442			retval = wb_tx_nobuf(device);
443		}
444
445		if (status & WB_ISR_TX_UNDERRUN) {
446			LOG(("WB_ISR_TX_UNDERRUN\n"));
447			// TODO: Jack up TX Threshold
448		}
449
450		if (status & WB_ISR_TX_IDLE) {
451			LOG(("WB_ISR_TX_IDLE\n"));
452		}
453
454		if (status & WB_ISR_TX_OK) {
455			LOG(("WB_ISR_TX_OK\n"));
456			// So what ?
457		}
458
459		if (status & WB_ISR_BUS_ERR) {
460			LOG(("WB_ISR_BUS_ERROR: %lx\n", (status & WB_ISR_BUSERRTYPE) >> 4));
461			//wb_reset(device);
462		}
463
464		if (status & WB_ISR_TIMER_EXPIRED) {
465			LOG(("WB_ISR_TIMER_EXPIRED\n"));
466			// ??
467		}
468	}
469
470	release_spinlock(&device->intLock);
471
472	return retval;
473}
474
475
476/*
477 * Print an ethernet address
478 */
479void
480print_address(ether_address_t* addr)
481{
482	int i;
483	char buf[3 * 6 + 1];
484
485	for (i = 0; i < 5; i++) {
486		sprintf(&buf[3 * i], "%02x:", addr->ebyte[i]);
487	}
488	sprintf(&buf[3 * 5], "%02x", addr->ebyte[5]);
489	dprintf("%s\n", buf);
490}
491
492
493status_t
494wb_create_semaphores(wb_device* device)
495{
496	device->rxSem = create_sem(0, "wb840 receive");
497	if (device->rxSem < B_OK) {
498		LOG(("Couldn't create sem, sem_id %ld\n", device->rxSem));
499		return device->rxSem;
500	}
501
502	device->txSem = create_sem(WB_TX_LIST_CNT, "wb840 transmit");
503	if (device->txSem < B_OK) {
504		LOG(("Couldn't create sem, sem_id %ld\n", device->txSem));
505		delete_sem(device->rxSem);
506		return device->txSem;
507	}
508
509	set_sem_owner(device->rxSem, B_SYSTEM_TEAM);
510	set_sem_owner(device->txSem, B_SYSTEM_TEAM);
511
512	device->rxLock = 0;
513	device->txLock = 0;
514
515	return B_OK;
516}
517
518
519void
520wb_delete_semaphores(wb_device* device)
521{
522	if (device->rxSem >= 0)
523		delete_sem(device->rxSem);
524	if (device->txSem >= 0)
525		delete_sem(device->txSem);
526}
527
528
529status_t
530wb_create_rings(wb_device* device)
531{
532	int i;
533
534	device->rxArea = create_area("wb840 rx buffer",
535		(void**)&device->rxBuffer[0], B_ANY_KERNEL_ADDRESS,
536		ROUND_TO_PAGE_SIZE(WB_BUFBYTES * WB_RX_LIST_CNT),
537		B_32_BIT_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
538	if (device->rxArea < B_OK)
539		return device->rxArea;
540
541	for (i = 1; i < WB_RX_LIST_CNT; i++) {
542		device->rxBuffer[i] = (void*)(((addr_t)device->rxBuffer[0])
543			+ (i * WB_BUFBYTES));
544	}
545
546	for (i = 0; i < WB_RX_LIST_CNT; i++) {
547		device->rxDescriptor[i].wb_status = 0;
548		device->rxDescriptor[i].wb_ctl = WB_RXCTL_RLINK;
549		wb_put_rx_descriptor(&device->rxDescriptor[i]);
550		device->rxDescriptor[i].wb_data = physicalAddress(
551			device->rxBuffer[i], WB_BUFBYTES);
552		device->rxDescriptor[i].wb_next = physicalAddress(
553			&device->rxDescriptor[(i + 1) & WB_RX_CNT_MASK],
554			sizeof(struct wb_desc));
555	}
556
557	device->rxFree = WB_RX_LIST_CNT;
558
559	device->txArea = create_area("wb840 tx buffer",
560		(void**)&device->txBuffer[0], B_ANY_KERNEL_ADDRESS,
561		ROUND_TO_PAGE_SIZE(WB_BUFBYTES * WB_TX_LIST_CNT),
562		B_32_BIT_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
563
564	if (device->txArea < B_OK) {
565		delete_area(device->rxArea);
566		return device->txArea;
567	}
568
569	for (i = 1; i < WB_TX_LIST_CNT; i++) {
570		device->txBuffer[i] = (void*)(((addr_t)device->txBuffer[0])
571			+ (i * WB_BUFBYTES));
572	}
573
574	for (i = 0; i < WB_TX_LIST_CNT; i++) {
575		device->txDescriptor[i].wb_status = 0;
576		device->txDescriptor[i].wb_ctl = WB_TXCTL_TLINK;
577		device->txDescriptor[i].wb_data = physicalAddress(
578			device->txBuffer[i], WB_BUFBYTES);
579		device->txDescriptor[i].wb_next = physicalAddress(
580			&device->txDescriptor[(i + 1) & WB_TX_CNT_MASK],
581			sizeof(struct wb_desc));
582	}
583
584	if (wb_stop(device) == B_OK) {
585		write32(device->reg_base + WB_RXADDR,
586			physicalAddress(&device->rxDescriptor[0], sizeof(struct wb_desc)));
587		write32(device->reg_base + WB_TXADDR,
588			physicalAddress(&device->txDescriptor[0], sizeof(struct wb_desc)));
589	}
590
591	return B_OK;
592}
593
594
595void
596wb_delete_rings(wb_device* device)
597{
598	delete_area(device->rxArea);
599	delete_area(device->txArea);
600}
601
602
603int32
604wb_read_mode(wb_device* info)
605{
606	uint16 autoAdv;
607	uint16 autoLinkPartner;
608	int32 speed;
609	int32 duplex;
610
611	uint16 status = mii_readstatus(info);
612	if (!(status & MII_STATUS_LINK)) {
613		LOG((DEVICE_NAME ": no link detected (status = %x)\n", status));
614		return 0;
615	}
616
617	// auto negotiation completed
618	autoAdv = wb_miibus_readreg(info, info->phy, MII_AUTONEG_ADV);
619	autoLinkPartner = wb_miibus_readreg(info, info->phy,
620		MII_AUTONEG_LINK_PARTNER);
621	status = autoAdv & autoLinkPartner;
622
623	speed = status & (MII_NWAY_TX | MII_NWAY_TX_FDX)
624		? LINK_SPEED_100_MBIT : LINK_SPEED_10_MBIT;
625	duplex = status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)
626		? LINK_FULL_DUPLEX : LINK_HALF_DUPLEX;
627
628	info->autoNegotiationComplete = true;
629
630	LOG((DEVICE_NAME ": linked, 10%s MBit, %s duplex\n",
631				speed == LINK_SPEED_100_MBIT ? "0" : "",
632				duplex == LINK_FULL_DUPLEX ? "full" : "half"));
633
634	return speed | duplex;
635}
636
637
638void
639wb_set_mode(wb_device* info, int mode)
640{
641	uint32 cfgAddress = (uint32)info->reg_base + WB_NETCFG;
642
643	uint32 configFlags = 0;
644	status_t status;
645
646	info->speed = mode & LINK_SPEED_MASK;
647	info->full_duplex = mode & LINK_DUPLEX_MASK;
648
649	status = wb_stop(info);
650
651	if ((mode & LINK_DUPLEX_MASK) == LINK_FULL_DUPLEX)
652		configFlags |= WB_NETCFG_FULLDUPLEX;
653
654	if (info->speed == LINK_SPEED_100_MBIT)
655		configFlags |= WB_NETCFG_100MBPS;
656
657	write32(cfgAddress, configFlags);
658
659	if (status == B_OK)
660		WB_SETBIT(cfgAddress, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON);
661}
662