BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / network / wb840 / wb840.c
blob4335d35037de97d8e1a7b0645dc77c3e15697a4c
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"
12 #include <ByteOrder.h>
13 #include <KernelExport.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
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
27 struct mii_chip_info
29 const char* name;
30 uint16 id0;
31 uint16 id1;
32 uint8 types;
36 const static struct mii_chip_info
37 gMIIChips[] = {
38 {"DAVICOM_DM9101", PHY_ID0_DAVICOM_DM9101, PHY_ID1_DAVICOM_DM9101, MII_LAN},
39 {NULL, 0, 0, 0}
43 static int
44 mii_readstatus(wb_device* device)
46 int i = 0;
47 int status;
49 // status bit has to be retrieved 2 times
50 while (i++ < 2)
51 status = wb_miibus_readreg(device, device->phy, MII_STATUS);
53 return status;
57 static phys_addr_t
58 physicalAddress(volatile void* addr, uint32 length)
60 physical_entry table;
62 get_memory_map((void*)addr, length, &table, 1);
64 return table.address;
68 // Prepares a RX descriptor to be used by the chip
69 void
70 wb_put_rx_descriptor(volatile wb_desc* descriptor)
72 descriptor->wb_status = WB_RXSTAT_OWN;
73 descriptor->wb_ctl |= WB_MAX_FRAMELEN | WB_RXCTL_RLINK;
77 void
78 wb_enable_interrupts(wb_device* device)
80 write32(device->reg_base + WB_IMR, WB_INTRS);
81 write32(device->reg_base + WB_ISR, 0xFFFFFFFF);
85 void
86 wb_disable_interrupts(wb_device* device)
88 write32(device->reg_base + WB_IMR, 0L);
89 write32(device->reg_base + WB_ISR, 0L);
93 static void
94 wb_selectPHY(wb_device* device)
96 uint16 status;
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;
104 wb_miibus_writereg(device, device->phy, MII_CONTROL, status);
106 wb_read_mode(device);
110 status_t
111 wb_initPHYs(wb_device* device)
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;
120 status = wb_miibus_readreg(device, phy, MII_STATUS);
121 status = wb_miibus_readreg(device, phy, MII_STATUS);
123 if (status == 0xffff || status == 0x0000)
124 // this MII is not accessable
125 continue;
127 mii = (struct mii_phy*)calloc(1, sizeof(struct mii_phy));
128 if (mii == NULL)
129 return B_NO_MEMORY;
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;
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;
145 i++;
147 if (gMIIChips[i].name == NULL) {
148 dprintf("Unknown MII PHY transceiver: id = (%x, %x).\n",
149 mii->id0, mii->id1);
153 if (device->firstPHY == NULL) {
154 dprintf("No MII PHY transceiver found!\n");
155 return B_ENTRY_NOT_FOUND;
158 wb_selectPHY(device);
159 device->link = mii_readstatus(device) & MII_STATUS_LINK;
161 return B_OK;
165 void
166 wb_init(wb_device* device)
168 LOG((DEVICE_NAME": init()\n"));
170 wb_reset(device);
172 device->wb_txthresh = WB_TXTHRESH_INIT;
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;
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);
194 write32(device->reg_base + WB_BUSCTL_SKIPLEN, WB_SKIPLEN_4LONG);
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));
201 wb_set_rx_filter(device);
205 void
206 wb_reset(wb_device *device)
208 int i = 0;
210 LOG((DEVICE_NAME": reset()\n"));
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);
217 WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
218 WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
220 for (i = 0; i < WB_TIMEOUT; i++) {
221 if (!(read32(device->reg_base + WB_BUSCTL) & WB_BUSCTL_RESET))
222 break;
225 if (i == WB_TIMEOUT)
226 LOG((DEVICE_NAME": reset hasn't completed!!!"));
228 /* Wait a bit while the chip reorders his toughts */
229 snooze(1000);
233 status_t
234 wb_stop(wb_device* device)
236 uint32 cfgAddress = (uint32)device->reg_base + WB_NETCFG;
237 int32 i = 0;
239 if (read32(cfgAddress) & (WB_NETCFG_TX_ON | WB_NETCFG_RX_ON)) {
240 WB_CLRBIT(cfgAddress, (WB_NETCFG_TX_ON | WB_NETCFG_RX_ON));
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;
249 if (i < WB_TIMEOUT)
250 return B_OK;
252 return B_ERROR;
256 static void
257 wb_updateLink(wb_device* device)
259 if (!device->autoNegotiationComplete) {
260 int32 mode = wb_read_mode(device);
261 if (mode)
262 wb_set_mode(device, mode);
264 return;
267 if (device->link) {
268 uint16 status = mii_readstatus(device);
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);
277 // Check if we have a new link
278 status = mii_readstatus(device);
279 if (status & MII_STATUS_LINK)
280 device->link = true;
285 int32
286 wb_tick(timer* arg)
288 wb_device* device = (wb_device*)arg;
290 wb_updateLink(device);
292 return B_OK;
297 * Program the rx filter.
299 void
300 wb_set_rx_filter(wb_device* device)
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);
308 /***************** Interrupt handling ******************************/
309 static status_t
310 wb_rxok(wb_device* device)
312 uint32 releaseRxSem = 0;
313 int16 limit;
315 acquire_spinlock(&device->rxSpinlock);
317 for (limit = device->rxFree; limit > 0; limit--) {
318 if (device->rxDescriptor[device->rxInterruptIndex].wb_status
319 & WB_RXSTAT_OWN) {
320 break;
323 releaseRxSem++;
324 device->rxInterruptIndex = (device->rxInterruptIndex + 1)
325 & WB_RX_CNT_MASK;
326 device->rxFree--;
329 // Re-enable receive queue
330 write32(device->reg_base + WB_RXSTART, 0xFFFFFFFF);
332 release_spinlock(&device->rxSpinlock);
334 if (releaseRxSem > 0) {
335 release_sem_etc(device->rxSem, releaseRxSem, B_DO_NOT_RESCHEDULE);
336 return B_INVOKE_SCHEDULER;
339 return B_HANDLED_INTERRUPT;
343 static status_t
344 wb_tx_nobuf(wb_device* info)
346 int16 releaseTxSem = 0;
347 int16 limit;
348 status_t status;
350 acquire_spinlock(&info->txSpinlock);
352 for (limit = info->txSent; limit > 0; limit--) {
353 status = info->txDescriptor[info->txInterruptIndex].wb_status;
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;
368 releaseTxSem++; // this many buffers are free
369 info->txInterruptIndex = (info->txInterruptIndex + 1) & WB_TX_CNT_MASK;
370 info->txSent--;
372 if (info->txSent < 0 || info->txSent > WB_TX_LIST_CNT)
373 dprintf("ERROR interrupt: txSent = %d\n", info->txSent);
376 release_spinlock(&info->txSpinlock);
378 if (releaseTxSem) {
379 release_sem_etc(info->txSem, releaseTxSem, B_DO_NOT_RESCHEDULE);
380 return B_INVOKE_SCHEDULER;
383 return B_HANDLED_INTERRUPT;
387 int32
388 wb_interrupt(void* arg)
390 wb_device* device = (wb_device*)arg;
391 int32 retval = B_UNHANDLED_INTERRUPT;
392 uint32 status;
394 // TODO: Handle other interrupts
396 acquire_spinlock(&device->intLock);
398 status = read32(device->reg_base + WB_ISR);
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);
406 if (status & WB_ISR_ABNORMAL)
407 LOG((DEVICE_NAME": *** Abnormal Interrupt received ***\n"));
408 else
409 LOG((DEVICE_NAME": interrupt received: \n"));
411 if (status & WB_ISR_RX_EARLY) {
412 LOG(("WB_ISR_RX_EARLY\n"));
415 if (status & WB_ISR_RX_NOBUF) {
416 LOG(("WB_ISR_RX_NOBUF\n"));
417 // Something is screwed
420 if (status & WB_ISR_RX_ERR) {
421 LOG(("WB_ISR_RX_ERR\n"));
422 // TODO: Do something useful
425 if (status & WB_ISR_RX_OK) {
426 LOG(("WB_ISR_RX_OK\n"));
427 retval = wb_rxok(device);
430 if (status & WB_ISR_RX_IDLE) {
431 LOG(("WB_ISR_RX_IDLE\n"));
432 // ???
435 if (status & WB_ISR_TX_EARLY) {
436 LOG(("WB_ISR_TX_EARLY\n"));
440 if (status & WB_ISR_TX_NOBUF) {
441 LOG(("WB_ISR_TX_NOBUF\n"));
442 retval = wb_tx_nobuf(device);
445 if (status & WB_ISR_TX_UNDERRUN) {
446 LOG(("WB_ISR_TX_UNDERRUN\n"));
447 // TODO: Jack up TX Threshold
450 if (status & WB_ISR_TX_IDLE) {
451 LOG(("WB_ISR_TX_IDLE\n"));
454 if (status & WB_ISR_TX_OK) {
455 LOG(("WB_ISR_TX_OK\n"));
456 // So what ?
459 if (status & WB_ISR_BUS_ERR) {
460 LOG(("WB_ISR_BUS_ERROR: %lx\n", (status & WB_ISR_BUSERRTYPE) >> 4));
461 //wb_reset(device);
464 if (status & WB_ISR_TIMER_EXPIRED) {
465 LOG(("WB_ISR_TIMER_EXPIRED\n"));
466 // ??
470 release_spinlock(&device->intLock);
472 return retval;
477 * Print an ethernet address
479 void
480 print_address(ether_address_t* addr)
482 int i;
483 char buf[3 * 6 + 1];
485 for (i = 0; i < 5; i++) {
486 sprintf(&buf[3 * i], "%02x:", addr->ebyte[i]);
488 sprintf(&buf[3 * 5], "%02x", addr->ebyte[5]);
489 dprintf("%s\n", buf);
493 status_t
494 wb_create_semaphores(wb_device* device)
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;
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;
509 set_sem_owner(device->rxSem, B_SYSTEM_TEAM);
510 set_sem_owner(device->txSem, B_SYSTEM_TEAM);
512 device->rxLock = 0;
513 device->txLock = 0;
515 return B_OK;
519 void
520 wb_delete_semaphores(wb_device* device)
522 if (device->rxSem >= 0)
523 delete_sem(device->rxSem);
524 if (device->txSem >= 0)
525 delete_sem(device->txSem);
529 status_t
530 wb_create_rings(wb_device* device)
532 int i;
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;
541 for (i = 1; i < WB_RX_LIST_CNT; i++) {
542 device->rxBuffer[i] = (void*)(((addr_t)device->rxBuffer[0])
543 + (i * WB_BUFBYTES));
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));
557 device->rxFree = WB_RX_LIST_CNT;
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);
564 if (device->txArea < B_OK) {
565 delete_area(device->rxArea);
566 return device->txArea;
569 for (i = 1; i < WB_TX_LIST_CNT; i++) {
570 device->txBuffer[i] = (void*)(((addr_t)device->txBuffer[0])
571 + (i * WB_BUFBYTES));
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));
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)));
591 return B_OK;
595 void
596 wb_delete_rings(wb_device* device)
598 delete_area(device->rxArea);
599 delete_area(device->txArea);
603 int32
604 wb_read_mode(wb_device* info)
606 uint16 autoAdv;
607 uint16 autoLinkPartner;
608 int32 speed;
609 int32 duplex;
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;
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;
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;
628 info->autoNegotiationComplete = true;
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"));
634 return speed | duplex;
638 void
639 wb_set_mode(wb_device* info, int mode)
641 uint32 cfgAddress = (uint32)info->reg_base + WB_NETCFG;
643 uint32 configFlags = 0;
644 status_t status;
646 info->speed = mode & LINK_SPEED_MASK;
647 info->full_duplex = mode & LINK_DUPLEX_MASK;
649 status = wb_stop(info);
651 if ((mode & LINK_DUPLEX_MASK) == LINK_FULL_DUPLEX)
652 configFlags |= WB_NETCFG_FULLDUPLEX;
654 if (info->speed == LINK_SPEED_100_MBIT)
655 configFlags |= WB_NETCFG_100MBPS;
657 write32(cfgAddress, configFlags);
659 if (status == B_OK)
660 WB_SETBIT(cfgAddress, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON);