iPXE
3c90x.c
Go to the documentation of this file.
1/*
2 * 3c90x.c -- This file implements a iPXE API 3c90x driver
3 *
4 * Originally written for etherboot by:
5 * Greg Beeley, Greg.Beeley@LightSys.org
6 * Modified by Steve Smith,
7 * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
8 * Almost totally Rewritten to use iPXE API, implementation of tx/rx ring support
9 * by Thomas Miletich, thomas.miletich@gmail.com
10 * Thanks to Marty Connor and Stefan Hajnoczi for their help and feedback,
11 * and to Daniel Verkamp for his help with testing.
12 *
13 * Copyright (c) 2009 Thomas Miletich
14 *
15 * Copyright (c) 1999 LightSys Technology Services, Inc.
16 * Portions Copyright (c) 1999 Steve Smith
17 *
18 * This program may be re-distributed in source or binary form, modified,
19 * sold, or copied for any purpose, provided that the above copyright message
20 * and this text are included with all source copies or derivative works, and
21 * provided that the above copyright message and this text are included in the
22 * documentation of any binary-only distributions. This program is distributed
23 * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
24 * PURPOSE or MERCHANTABILITY. Please read the associated documentation
25 * "3c90x.txt" before compiling and using this driver.
26 *
27 * [ --mdc 20090313 The 3c90x.txt file is now at:
28 * http://etherboot.org/wiki/appnotes/3c90x_issues ]
29 *
30 * This program was written with the assistance of the 3com documentation for
31 * the 3c905B-TX card, as well as with some assistance from the 3c59x
32 * driver Donald Becker wrote for the Linux kernel, and with some assistance
33 * from the remainder of the Etherboot distribution.
34 *
35 * Indented with unix 'indent' command:
36 * $ indent -kr -i8 3c90x.c
37 */
38
39FILE_LICENCE ( BSD2 );
40
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <stddef.h>
45#include <string.h>
46#include <unistd.h>
47#include <assert.h>
48#include <byteswap.h>
49#include <errno.h>
50#include <ipxe/ethernet.h>
51#include <ipxe/if_ether.h>
52#include <ipxe/io.h>
53#include <ipxe/iobuf.h>
54#include <ipxe/malloc.h>
55#include <ipxe/netdevice.h>
56#include <ipxe/pci.h>
57#include <ipxe/timer.h>
58#include <ipxe/nvs.h>
59
60#include "3c90x.h"
61
62/**
63 * a3c90x_internal_IssueCommand: sends a command to the 3c90x card
64 * and waits for it's completion
65 *
66 * @v ioaddr IOAddress of the NIC
67 * @v cmd Command to be issued
68 * @v param Command parameter
69 */
70static void a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
71{
72 unsigned int val = (cmd << 11) | param;
73 int cnt = 0;
74
75 DBGP("a3c90x_internal_IssueCommand\n");
76
77 /* Send the cmd to the cmd register */
79
80 /* Wait for the cmd to complete */
81 for (cnt = 0; cnt < 100000; cnt++) {
83 continue;
84 } else {
85 DBG2("Command 0x%04X finished in time. cnt = %d.\n", cmd, cnt);
86 return;
87 }
88 }
89
90 DBG("Command 0x%04X DID NOT finish in time. cnt = %d.\n", cmd, cnt);
91}
92
93/**
94 * a3c90x_internal_SetWindow: selects a register window set.
95 *
96 * @v inf_3c90x private NIC data
97 * @v window window to be selected
98 */
99static void a3c90x_internal_SetWindow(struct INF_3C90X *inf_3c90x, int window)
100{
101 DBGP("a3c90x_internal_SetWindow\n");
102 /* Window already as set? */
103 if (inf_3c90x->CurrentWindow == window)
104 return;
105
106 /* Issue the window command. */
109 inf_3c90x->CurrentWindow = window;
110
111 return;
112}
113
114static void a3c90x_internal_WaitForEeprom(struct INF_3C90X *inf_3c90x)
115{
116 int cnt = 0;
117
118 DBGP("a3c90x_internal_WaitForEeprom\n");
119
120 while (eepromBusy & inw(inf_3c90x->IOAddr + regEepromCommand_0_w)) {
121 if (cnt == EEPROM_TIMEOUT) {
122 DBG("Read from eeprom failed: timeout\n");
123 return;
124 }
125 udelay(1);
126 cnt++;
127 }
128}
129
130/**
131 * a3c90x_internal_ReadEeprom - nvs routine to read eeprom data
132 * We only support reading one word(2 byte). The nvs subsystem will make sure
133 * that the routine will never be called with len != 2.
134 *
135 * @v nvs nvs data.
136 * @v address eeprom address to read data from.
137 * @v data data is put here.
138 * @v len number of bytes to read.
139 */
140static int
141a3c90x_internal_ReadEeprom(struct nvs_device *nvs, unsigned int address, void *data, size_t len)
142{
143 unsigned short *dest = (unsigned short *) data;
144 struct INF_3C90X *inf_3c90x =
145 container_of(nvs, struct INF_3C90X, nvs);
146
147 DBGP("a3c90x_internal_ReadEeprom\n");
148
149 /* we support reading 2 bytes only */
150 assert(len == 2);
151
152 /* Select correct window */
154
155 /* set eepromRead bits in command sent to NIC */
156 address += (inf_3c90x->is3c556 ? eepromRead_556 : eepromRead);
157
159 /* send address to NIC */
162
163 /* read value */
164 *dest = inw(inf_3c90x->IOAddr + regEepromData_0_w);
165
166 return 0;
167}
168
169/**
170 * a3c90x_internal_WriteEeprom - nvs routine to write eeprom data
171 * currently not implemented
172 *
173 * @v nvs nvs data.
174 * @v address eeprom address to read data from.
175 * @v data data is put here.
176 * @v len number of bytes to read.
177 */
178static int
180 unsigned int address __unused,
181 const void *data __unused, size_t len __unused)
182{
183 return -ENOTSUP;
184}
185
186static void a3c90x_internal_ReadEepromContents(struct INF_3C90X *inf_3c90x)
187{
188 int eeprom_size = (inf_3c90x->isBrev ? 0x20 : 0x17) * 2;
189
190 DBGP("a3c90x_internal_ReadEepromContents\n");
191
192 nvs_read(&inf_3c90x->nvs, 0, inf_3c90x->eeprom, eeprom_size);
193}
194
195/**
196 * a3c90x_reset: exported function that resets the card to its default
197 * state. This is so the Linux driver can re-set the card up the way
198 * it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
199 * not alter the selected transceiver that we used to download the boot
200 * image.
201 *
202 * @v inf_3c90x Private NIC data
203 */
204static void a3c90x_reset(struct INF_3C90X *inf_3c90x)
205{
206 DBGP("a3c90x_reset\n");
207 /* Send the reset command to the card */
208 DBG2("3c90x: Issuing RESET\n");
209
210 /* reset of the receiver on B-revision cards re-negotiates the link
211 * takes several seconds (a computer eternity), so we don't reset
212 * it here.
213 */
217
218 /* global reset command resets station mask, non-B revision cards
219 * require explicit reset of values
220 */
222 outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 0);
223 outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 2);
224 outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 4);
225
228
229 /* enable rxComplete and txComplete indications */
233
234 /* acknowledge any pending status flags */
237
238 return;
239}
240
241/**
242 * a3c90x_setup_tx_ring - Allocates TX ring, initialize tx_desc values
243 *
244 * @v p Private NIC data
245 *
246 * @ret Returns 0 on success, negative on failure
247 */
248static int a3c90x_setup_tx_ring(struct INF_3C90X *p)
249{
250 DBGP("a3c90x_setup_tx_ring\n");
251 p->tx_ring =
252 malloc_phys(TX_RING_SIZE * sizeof(struct TXD), TX_RING_ALIGN);
253
254 if (!p->tx_ring) {
255 DBG("Could not allocate TX-ring\n");
256 return -ENOMEM;
257 }
258
259 memset(p->tx_ring, 0, TX_RING_SIZE * sizeof(struct TXD));
260 p->tx_cur = 0;
261 p->tx_cnt = 0;
262 p->tx_tail = 0;
263
264 return 0;
265}
266
267/**
268 * a3c90x_process_tx_packets - Checks for successfully sent packets,
269 * reports them to iPXE with netdev_tx_complete();
270 *
271 * @v netdev Network device info
272 */
274{
275 struct INF_3C90X *p = netdev->priv;
276 unsigned int downlist_ptr;
277
278 DBGP("a3c90x_process_tx_packets\n");
279
280 DBG2(" tx_cnt: %d\n", p->tx_cnt);
281
282 while (p->tx_tail != p->tx_cur) {
283
284 downlist_ptr = inl(p->IOAddr + regDnListPtr_l);
285
286 DBG2(" downlist_ptr: %#08x\n", downlist_ptr);
287 DBG2(" tx_tail: %d tx_cur: %d\n", p->tx_tail, p->tx_cur);
288
289 /* NIC is currently working on this tx desc */
290 if(downlist_ptr == virt_to_bus(p->tx_ring + p->tx_tail))
291 return;
292
294
295 DBG2("transmitted packet\n");
296 DBG2(" size: %zd\n", iob_len(p->tx_iobuf[p->tx_tail]));
297
298 p->tx_tail = (p->tx_tail + 1) % TX_RING_SIZE;
299 p->tx_cnt--;
300 }
301}
302
303static void a3c90x_free_tx_ring(struct INF_3C90X *p)
304{
305 DBGP("a3c90x_free_tx_ring\n");
306
307 free_phys(p->tx_ring, TX_RING_SIZE * sizeof(struct TXD));
308 p->tx_ring = NULL;
309 /* io_buffers are free()ed by netdev_tx_complete[,_err]() */
310}
311
312/**
313 * a3c90x_transmit - Transmits a packet.
314 *
315 * @v netdev Network device info
316 * @v iob io_buffer containing the data to be send
317 *
318 * @ret Returns 0 on success, negative on failure
319 */
321 struct io_buffer *iob)
322{
323 struct INF_3C90X *inf_3c90x = netdev->priv;
324 struct TXD *tx_cur_desc;
325 struct TXD *tx_prev_desc;
326
327 unsigned int len;
328 unsigned int downlist_ptr;
329
330 DBGP("a3c90x_transmit\n");
331
332 if (inf_3c90x->tx_cnt == TX_RING_SIZE) {
333 DBG("TX-Ring overflow\n");
334 return -ENOBUFS;
335 }
336
337 inf_3c90x->tx_iobuf[inf_3c90x->tx_cur] = iob;
338 tx_cur_desc = inf_3c90x->tx_ring + inf_3c90x->tx_cur;
339
340 tx_prev_desc = inf_3c90x->tx_ring +
341 (((inf_3c90x->tx_cur + TX_RING_SIZE) - 1) % TX_RING_SIZE);
342
343 len = iob_len(iob);
344
345 /* Setup the DPD (download descriptor) */
346 tx_cur_desc->DnNextPtr = 0;
347
348 /* FrameStartHeader differs in 90x and >= 90xB
349 * It contains the packet length in 90x and a round up boundary and
350 * packet ID for 90xB and 90xC. Disable packet length round-up on the
351 * later revisions.
352 */
353 tx_cur_desc->FrameStartHeader =
354 fshTxIndicate | (inf_3c90x->isBrev ? fshRndupDefeat : len);
355
356 tx_cur_desc->DataAddr = virt_to_bus(iob->data);
357 tx_cur_desc->DataLength = len | downLastFrag;
358
359 /* We have to stall the download engine, so the NIC won't access the
360 * tx descriptor while we modify it. There is a way around this
361 * from revision B and upwards. To stay compatible with older revisions
362 * we don't use it here.
363 */
365 dnStall);
366
367 tx_prev_desc->DnNextPtr = virt_to_bus(tx_cur_desc);
368
369 downlist_ptr = inl(inf_3c90x->IOAddr + regDnListPtr_l);
370 if (downlist_ptr == 0) {
371 /* currently no DownList, sending a new one */
372 outl(virt_to_bus(tx_cur_desc),
373 inf_3c90x->IOAddr + regDnListPtr_l);
374 }
375
376 /* End Stall */
378 dnUnStall);
379
380 inf_3c90x->tx_cur = (inf_3c90x->tx_cur + 1) % TX_RING_SIZE;
381 inf_3c90x->tx_cnt++;
382
383 return 0;
384}
385
386/**
387 * a3c90x_prepare_rx_desc - fills the rx desc with initial data
388 *
389 * @v p NIC private data
390 * @v index Index for rx_iobuf and rx_ring array
391 */
392
393static void a3c90x_prepare_rx_desc(struct INF_3C90X *p, unsigned int index)
394{
395 DBGP("a3c90x_prepare_rx_desc\n");
396 DBG2("Populating rx_desc %d\n", index);
397
398 /* We have to stall the upload engine, so the NIC won't access the
399 * rx descriptor while we modify it. There is a way around this
400 * from revision B and upwards. To stay compatible with older revisions
401 * we don't use it here.
402 */
404
407 p->rx_ring[index].UpPktStatus = 0;
408
409 /* unstall upload engine */
411}
412
413/**
414 * a3c90x_refill_rx_ring -checks every entry in the rx ring and reallocates
415 * them as necessary. Then it calls a3c90x_prepare_rx_desc to fill the rx desc
416 * with initial data.
417 *
418 * @v p NIC private data
419 */
420static void a3c90x_refill_rx_ring(struct INF_3C90X *p)
421{
422 int i;
423 unsigned int status;
424 struct RXD *rx_cur_desc;
425
426 DBGP("a3c90x_refill_rx_ring\n");
427
428 for (i = 0; i < RX_RING_SIZE; i++) {
429 rx_cur_desc = p->rx_ring + i;
430 status = rx_cur_desc->UpPktStatus;
431
432 /* only refill used descriptor */
433 if (!(status & upComplete))
434 continue;
435
436 /* we still need to process this descriptor */
437 if (p->rx_iobuf[i] != NULL)
438 continue;
439
441 if (p->rx_iobuf[i] == NULL) {
442 DBG("alloc_iob() failed\n");
443 break;
444 }
445
447 }
448}
449
450/**
451 * a3c90x_setup_rx_ring - Allocates RX ring, initialize rx_desc values
452 *
453 * @v p Private NIC data
454 *
455 * @ret Returns 0 on success, negative on failure
456 */
457static int a3c90x_setup_rx_ring(struct INF_3C90X *p)
458{
459 int i;
460
461 DBGP("a3c90x_setup_rx_ring\n");
462
463 p->rx_ring =
464 malloc_phys(RX_RING_SIZE * sizeof(struct RXD), RX_RING_ALIGN);
465
466 if (!p->rx_ring) {
467 DBG("Could not allocate RX-ring\n");
468 return -ENOMEM;
469 }
470
471 p->rx_cur = 0;
472
473 for (i = 0; i < RX_RING_SIZE; i++) {
474 p->rx_ring[i].UpNextPtr =
475 virt_to_bus(p->rx_ring + (i + 1));
476
477 /* these are needed so refill_rx_ring initializes the ring */
479 p->rx_iobuf[i] = NULL;
480 }
481
482 /* Loop the ring */
483 p->rx_ring[i - 1].UpNextPtr = virt_to_bus(p->rx_ring);
484
486
487 return 0;
488}
489
490static void a3c90x_free_rx_ring(struct INF_3C90X *p)
491{
492 DBGP("a3c90x_free_rx_ring\n");
493
494 free_phys(p->rx_ring, RX_RING_SIZE * sizeof(struct RXD));
495 p->rx_ring = NULL;
496}
497
498static void a3c90x_free_rx_iobuf(struct INF_3C90X *p)
499{
500 int i;
501
502 DBGP("a3c90x_free_rx_iobuf\n");
503
504 for (i = 0; i < RX_RING_SIZE; i++) {
505 free_iob(p->rx_iobuf[i]);
506 p->rx_iobuf[i] = NULL;
507 }
508}
509
510/**
511 * a3c90x_process_rx_packets - Checks for received packets,
512 * reports them to iPXE with netdev_rx() or netdev_rx_err() if there was an
513 * error while receiving the packet
514 *
515 * @v netdev Network device info
516 */
518{
519 int i;
520 unsigned int rx_status;
521 struct INF_3C90X *p = netdev->priv;
522 struct RXD *rx_cur_desc;
523
524 DBGP("a3c90x_process_rx_packets\n");
525
526 for (i = 0; i < RX_RING_SIZE; i++) {
527 rx_cur_desc = p->rx_ring + p->rx_cur;
528 rx_status = rx_cur_desc->UpPktStatus;
529
530 if (!(rx_status & upComplete) && !(rx_status & upError))
531 break;
532
533 if (p->rx_iobuf[p->rx_cur] == NULL)
534 break;
535
536 if (rx_status & upError) {
537 DBG("Corrupted packet received: %#x\n", rx_status);
539 -EINVAL);
540 } else {
541 /* if we're here, we've got good packet */
542 int packet_len;
543
544 packet_len = rx_status & 0x1FFF;
545 iob_put(p->rx_iobuf[p->rx_cur], packet_len);
546
547 DBG2("received packet\n");
548 DBG2(" size: %d\n", packet_len);
549
551 }
552
553 p->rx_iobuf[p->rx_cur] = NULL; /* invalidate rx desc */
554 p->rx_cur = (p->rx_cur + 1) % RX_RING_SIZE;
555 }
557
558}
559
560/**
561 * a3c90x_poll - Routine that gets called periodically.
562 * Here we hanle transmitted and received packets.
563 * We could also check the link status from time to time, which we
564 * currently don't do.
565 *
566 * @v netdev Network device info
567 */
568static void a3c90x_poll(struct net_device *netdev)
569{
570 struct INF_3C90X *p = netdev->priv;
571 uint16_t raw_status, int_status;
572
573 DBGP("a3c90x_poll\n");
574
575 raw_status = inw(p->IOAddr + regCommandIntStatus_w);
576 int_status = (raw_status & 0x0FFF);
577
578 if ( int_status == 0 )
579 return;
580
582 int_status);
583
584 if (int_status & INT_TXCOMPLETE)
585 outb(0x00, p->IOAddr + regTxStatus_b);
586
587 DBG2("poll: status = %#04x\n", raw_status);
588
590
592}
593
594
595
596static void a3c90x_free_resources(struct INF_3C90X *p)
597{
598 DBGP("a3c90x_free_resources\n");
599
603}
604
605/**
606 * a3c90x_remove - Routine to remove the card. Unregisters
607 * the NIC from iPXE, disables RX/TX and resets the card.
608 *
609 * @v pci PCI device info
610 */
611static void a3c90x_remove(struct pci_device *pci)
612{
613 struct net_device *netdev = pci_get_drvdata(pci);
614 struct INF_3C90X *inf_3c90x = netdev->priv;
615
616 DBGP("a3c90x_remove\n");
617
618 a3c90x_reset(inf_3c90x);
619
620 /* Disable the receiver and transmitter. */
623
627}
628
629static void a3c90x_irq(struct net_device *netdev, int enable)
630{
631 struct INF_3C90X *p = netdev->priv;
632
633 DBGP("a3c90x_irq\n");
634
635 if (enable == 0) {
636 /* disable interrupts */
639 } else {
646 0x661);
647 }
648}
649
650/**
651 * a3c90x_hw_start - Initialize hardware, copy MAC address
652 * to NIC registers, set default receiver
653 */
655{
656 int i, c;
657 unsigned int cfg;
658 unsigned int mopt;
659 unsigned short linktype;
660 struct INF_3C90X *inf_3c90x = netdev->priv;
661
662 DBGP("a3c90x_hw_start\n");
663
664 /* 3C556: Invert MII power */
665 if (inf_3c90x->is3c556) {
666 unsigned int tmp;
668 tmp = inw(inf_3c90x->IOAddr + regResetOptions_2_w);
669 tmp |= 0x4000;
670 outw(tmp, inf_3c90x->IOAddr + regResetOptions_2_w);
671 }
672
673 /* Copy MAC address into the NIC registers */
675 for (i = 0; i < ETH_ALEN; i++)
676 outb(netdev->ll_addr[i],
677 inf_3c90x->IOAddr + regStationAddress_2_3w + i);
678 for (i = 0; i < ETH_ALEN; i++)
679 outb(0, inf_3c90x->IOAddr + regStationMask_2_3w + i);
680
681 /* Read the media options register, print a message and set default
682 * xcvr.
683 *
684 * Uses Media Option command on B revision, Reset Option on non-B
685 * revision cards -- same register address
686 */
688 mopt = inw(inf_3c90x->IOAddr + regResetMediaOptions_3_w);
689
690 /* mask out VCO bit that is defined as 10baseFL bit on B-rev cards */
691 if (!inf_3c90x->isBrev) {
692 mopt &= 0x7F;
693 }
694
695 DBG2("Connectors present: ");
696 c = 0;
697 linktype = 0x0008;
698 if (mopt & 0x01) {
699 DBG2("%s100Base-T4", (c++) ? ", " : "");
701 }
702 if (mopt & 0x04) {
703 DBG2("%s100Base-FX", (c++) ? ", " : "");
705 }
706 if (mopt & 0x10) {
707 DBG2("%s10Base-2", (c++) ? ", " : "");
709 }
710 if (mopt & 0x20) {
711 DBG2("%sAUI", (c++) ? ", " : "");
713 }
714 if (mopt & 0x40) {
715 DBG2("%sMII", (c++) ? ", " : "");
717 }
718 if ((mopt & 0xA) == 0xA) {
719 DBG2("%s10Base-T / 100Base-TX", (c++) ? ", " : "");
721 } else if ((mopt & 0xA) == 0x2) {
722 DBG2("%s100Base-TX", (c++) ? ", " : "");
724 } else if ((mopt & 0xA) == 0x8) {
725 DBG2("%s10Base-T", (c++) ? ", " : "");
727 }
728 DBG2(".\n");
729
730 /* Determine transceiver type to use, depending on value stored in
731 * eeprom 0x16
732 */
733 if (inf_3c90x->isBrev) {
734 if ((inf_3c90x->eeprom[0x16] & 0xFF00) == XCVR_MAGIC) {
735 /* User-defined */
736 linktype = inf_3c90x->eeprom[0x16] & 0x000F;
737 }
738 } else {
739 /* I don't know what MII MAC only mode is!!! */
740 if (linktype == linkExternalMII) {
741 if (inf_3c90x->isBrev)
742 DBG("WARNING: MII External MAC Mode only supported on B-revision " "cards!!!!\nFalling Back to MII Mode\n");
744 }
745 }
746
747 /* enable DC converter for 10-Base-T */
748 if (linktype == link10Base2) {
751 }
752
753 /* Set the link to the type we just determined. */
755 cfg = inl(inf_3c90x->IOAddr + regInternalConfig_3_l);
756 cfg &= ~(0xF << 20);
757 cfg |= (linktype << 20);
758
759 DBG2("Setting internal cfg register: 0x%08X (linktype: 0x%02X)\n",
760 cfg, linktype);
761
762 outl(cfg, inf_3c90x->IOAddr + regInternalConfig_3_l);
763
764 /* Now that we set the xcvr type, reset the Tx and Rx */
766
767 if (!inf_3c90x->isBrev)
768 outb(0x01, inf_3c90x->IOAddr + regTxFreeThresh_b);
769
770 /* Set the RX filter = receive only individual pkts & multicast & bcast. */
772 0x01 + 0x02 + 0x04);
773
774
775 /*
776 * set Indication and Interrupt flags , acknowledge any IRQ's
777 */
786}
787
788/**
789 * a3c90x_open - Routine to initialize the card. Initialize hardware,
790 * allocate TX and RX ring, send RX ring address to the NIC.
791 *
792 * @v netdev Network device info
793 *
794 * @ret Returns 0 on success, negative on failure
795 */
796static int a3c90x_open(struct net_device *netdev)
797{
798 int rc;
799 struct INF_3C90X *inf_3c90x = netdev->priv;
800
801 DBGP("a3c90x_open\n");
802
804
805 rc = a3c90x_setup_tx_ring(inf_3c90x);
806 if (rc != 0) {
807 DBG("Error setting up TX Ring\n");
808 goto error;
809 }
810
811 rc = a3c90x_setup_rx_ring(inf_3c90x);
812 if (rc != 0) {
813 DBG("Error setting up RX Ring\n");
814 goto error;
815 }
816
818
819 /* send rx_ring address to NIC */
820 outl(virt_to_bus(inf_3c90x->rx_ring),
821 inf_3c90x->IOAddr + regUpListPtr_l);
822
824
825 /* set maximum allowed receive packet length */
828
829 /* enable packet transmission and reception */
832
833 return 0;
834
835 error:
836 a3c90x_free_resources(inf_3c90x);
837 a3c90x_reset(inf_3c90x);
838 return rc;
839}
840
841/**
842 * a3c90x_close - free()s TX and RX ring, disablex RX/TX, resets NIC
843 *
844 * @v netdev Network device info
845 */
846static void a3c90x_close(struct net_device *netdev)
847{
848 struct INF_3C90X *inf_3c90x = netdev->priv;
849
850 DBGP("a3c90x_close\n");
851
852 a3c90x_reset(inf_3c90x);
855 a3c90x_free_resources(inf_3c90x);
856}
857
859 .open = a3c90x_open,
860 .close = a3c90x_close,
861 .poll = a3c90x_poll,
862 .transmit = a3c90x_transmit,
863 .irq = a3c90x_irq,
864};
865
866/**
867 * a3c90x_probe: exported routine to probe for the 3c905 card.
868 * If this routine is called, the pci functions did find the
869 * card. We read the eeprom here and get the MAC address.
870 * Initialization is done in a3c90x_open().
871 *
872 * @v pci PCI device info
873 * @ pci_id PCI device IDs
874 *
875 * @ret rc Returns 0 on success, negative on failure
876 */
877static int a3c90x_probe(struct pci_device *pci)
878{
879
880 struct net_device *netdev;
881 struct INF_3C90X *inf_3c90x;
882 unsigned char *HWAddr;
883 int rc;
884
885 DBGP("a3c90x_probe\n");
886
887 if (pci->ioaddr == 0)
888 return -EINVAL;
889
890 netdev = alloc_etherdev(sizeof(*inf_3c90x));
891 if (!netdev)
892 return -ENOMEM;
893
896 netdev->dev = &pci->dev;
897
898 inf_3c90x = netdev->priv;
899 memset(inf_3c90x, 0, sizeof(*inf_3c90x));
900
902
903 inf_3c90x->is3c556 = (pci->device == 0x6055);
904 inf_3c90x->IOAddr = pci->ioaddr;
905 inf_3c90x->CurrentWindow = winNone;
906
907 inf_3c90x->isBrev = 1;
908 switch (pci->device) {
909 case 0x9000: /* 10 Base TPO */
910 case 0x9001: /* 10/100 T4 */
911 case 0x9050: /* 10/100 TPO */
912 case 0x9051: /* 10 Base Combo */
913 inf_3c90x->isBrev = 0;
914 break;
915 }
916
917 DBG2("[3c90x]: found NIC(0x%04X, 0x%04X), isBrev=%d, is3c556=%d\n",
918 pci->vendor, pci->device, inf_3c90x->isBrev,
919 inf_3c90x->is3c556);
920
921 /* initialize nvs device */
922 inf_3c90x->nvs.word_len_log2 = 1; /* word */
923 inf_3c90x->nvs.size = (inf_3c90x->isBrev ? 0x20 : 0x17);
924 inf_3c90x->nvs.block_size = 1;
927
928 /* reset NIC before accessing any data from it */
929 a3c90x_reset(inf_3c90x);
930
931 /* load eeprom contents to inf_3c90x->eeprom */
933
934 HWAddr = netdev->hw_addr;
935
936 /* Retrieve the Hardware address */
937 HWAddr[0] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] >> 8;
938 HWAddr[1] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] & 0xFF;
939 HWAddr[2] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] >> 8;
940 HWAddr[3] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] & 0xFF;
941 HWAddr[4] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] >> 8;
942 HWAddr[5] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] & 0xFF;
943
944 if ((rc = register_netdev(netdev)) != 0) {
945 DBG("3c90x: register_netdev() failed\n");
947 return rc;
948 }
949
950 /* we don't handle linkstates yet, so we're always up */
952
953 return 0;
954}
955
956static struct pci_device_id a3c90x_nics[] = {
957/* Original 90x revisions: */
958 PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A", 0),
959 PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B", 0),
960 PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado", 0),
961 PCI_ROM(0x10b7, 0x6055, "3c556", "3C556", 0), /* Huricane */
962 PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX", 0), /* Hurricane */
963 PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO", 0), /* 10 Base TPO */
964 PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo", 0), /* 10/100 T4 */
965/* Newer 90xB revisions: */
966 PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO", 0), /* 10 Base TPO */
967 PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo", 0), /* 10 Base Combo */
968 PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T", 0), /* 10 Base TP and Base2 */
969 PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL", 0), /* 10 Base FL */
970 PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX", 0), /* 100 Base TX / 10/100 TPO */
971 PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4", 0), /* 100 Base T4 / 10 Base Combo */
972 PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX", 0), /* 10/100 TPO */
973 PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4", 0), /* 10/100 T4 */
974 PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058", 0), /* Cyclone 10/100/BNC */
975 PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL", 0), /* 100 Base FX / 10 Base FX */
976/* Newer 90xC revision: */
977 PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM", 0), /* 10/100 TPO (3C905C-TXM) */
978 PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)", 0), /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
979 PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm", "3Com20B-EMB WNM", 0),
980 PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone", 0), /* Cyclone */
981 PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805", 0), /* Dual Port Server Cyclone */
982};
983
984struct pci_driver a3c90x_driver __pci_driver = {
985 .ids = a3c90x_nics,
986 .id_count = (sizeof(a3c90x_nics) / sizeof(a3c90x_nics[0])),
989};
990
991/*
992 * Local variables:
993 * c-basic-offset: 8
994 * c-indent-level: 8
995 * tab-width: 8
996 * End:
997 */
#define TX_RING_SIZE
Definition 3c515.c:85
#define RX_RING_SIZE
Definition 3c515.c:86
static void a3c90x_refill_rx_ring(struct INF_3C90X *p)
a3c90x_refill_rx_ring -checks every entry in the rx ring and reallocates them as necessary.
Definition 3c90x.c:420
static void a3c90x_free_tx_ring(struct INF_3C90X *p)
Definition 3c90x.c:303
static int a3c90x_transmit(struct net_device *netdev, struct io_buffer *iob)
a3c90x_transmit - Transmits a packet.
Definition 3c90x.c:320
static void a3c90x_internal_WaitForEeprom(struct INF_3C90X *inf_3c90x)
Definition 3c90x.c:114
static int a3c90x_setup_rx_ring(struct INF_3C90X *p)
a3c90x_setup_rx_ring - Allocates RX ring, initialize rx_desc values
Definition 3c90x.c:457
static void a3c90x_free_rx_iobuf(struct INF_3C90X *p)
Definition 3c90x.c:498
static void a3c90x_internal_SetWindow(struct INF_3C90X *inf_3c90x, int window)
a3c90x_internal_SetWindow: selects a register window set.
Definition 3c90x.c:99
static int a3c90x_setup_tx_ring(struct INF_3C90X *p)
a3c90x_setup_tx_ring - Allocates TX ring, initialize tx_desc values
Definition 3c90x.c:248
static int a3c90x_internal_WriteEeprom(struct nvs_device *nvs __unused, unsigned int address __unused, const void *data __unused, size_t len __unused)
a3c90x_internal_WriteEeprom - nvs routine to write eeprom data currently not implemented
Definition 3c90x.c:179
static void a3c90x_free_resources(struct INF_3C90X *p)
Definition 3c90x.c:596
static void a3c90x_hw_start(struct net_device *netdev)
a3c90x_hw_start - Initialize hardware, copy MAC address to NIC registers, set default receiver
Definition 3c90x.c:654
static int a3c90x_open(struct net_device *netdev)
a3c90x_open - Routine to initialize the card.
Definition 3c90x.c:796
static struct net_device_operations a3c90x_operations
Definition 3c90x.c:858
static void a3c90x_poll(struct net_device *netdev)
a3c90x_poll - Routine that gets called periodically.
Definition 3c90x.c:568
static struct pci_device_id a3c90x_nics[]
Definition 3c90x.c:956
static void a3c90x_prepare_rx_desc(struct INF_3C90X *p, unsigned int index)
a3c90x_prepare_rx_desc - fills the rx desc with initial data
Definition 3c90x.c:393
static void a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
a3c90x_internal_IssueCommand: sends a command to the 3c90x card and waits for it's completion
Definition 3c90x.c:70
static int a3c90x_internal_ReadEeprom(struct nvs_device *nvs, unsigned int address, void *data, size_t len)
a3c90x_internal_ReadEeprom - nvs routine to read eeprom data We only support reading one word(2 byte)...
Definition 3c90x.c:141
static void a3c90x_internal_ReadEepromContents(struct INF_3C90X *inf_3c90x)
Definition 3c90x.c:186
static void a3c90x_remove(struct pci_device *pci)
a3c90x_remove - Routine to remove the card.
Definition 3c90x.c:611
static void a3c90x_irq(struct net_device *netdev, int enable)
Definition 3c90x.c:629
static void a3c90x_process_rx_packets(struct net_device *netdev)
a3c90x_process_rx_packets - Checks for received packets, reports them to iPXE with netdev_rx() or net...
Definition 3c90x.c:517
static int a3c90x_probe(struct pci_device *pci)
a3c90x_probe: exported routine to probe for the 3c905 card.
Definition 3c90x.c:877
static void a3c90x_free_rx_ring(struct INF_3C90X *p)
Definition 3c90x.c:490
static void a3c90x_process_tx_packets(struct net_device *netdev)
a3c90x_process_tx_packets - Checks for successfully sent packets, reports them to iPXE with netdev_tx...
Definition 3c90x.c:273
static void a3c90x_reset(struct INF_3C90X *inf_3c90x)
a3c90x_reset: exported function that resets the card to its default state.
Definition 3c90x.c:204
static void a3c90x_close(struct net_device *netdev)
a3c90x_close - free()s TX and RX ring, disablex RX/TX, resets NIC
Definition 3c90x.c:846
#define XCVR_MAGIC
Definition 3c90x.h:55
#define INT_UPCOMPLETE
Definition 3c90x.h:260
linktype
Definition 3c90x.h:240
@ linkAutoneg
Definition 3c90x.h:246
@ linkAUI
Definition 3c90x.h:242
@ link10Base2
Definition 3c90x.h:243
@ linkMII
Definition 3c90x.h:245
@ linkExternalMII
Definition 3c90x.h:247
@ link100BaseFX
Definition 3c90x.h:244
@ cmdTxReset
Definition 3c90x.h:181
@ cmdSetInterruptEnable
Definition 3c90x.h:184
@ cmdAcknowledgeInterrupt
Definition 3c90x.h:183
@ cmdSelectRegisterWindow
Definition 3c90x.h:173
@ cmdTxDisable
Definition 3c90x.h:180
@ cmdGlobalReset
Definition 3c90x.h:172
@ cmdRxEnable
Definition 3c90x.h:176
@ cmdTxEnable
Definition 3c90x.h:179
@ cmdSetRxFilter
Definition 3c90x.h:186
@ cmdEnableDcConverter
Definition 3c90x.h:174
@ cmdSetIndicationEnable
Definition 3c90x.h:185
@ cmdRxDisable
Definition 3c90x.h:175
@ cmdStallCtl
Definition 3c90x.h:178
@ regUpListPtr_l
Definition 3c90x.h:68
@ regCommandIntStatus_w
Definition 3c90x.h:83
@ regDnListPtr_l
Definition 3c90x.h:76
@ regTxStatus_b
Definition 3c90x.h:80
@ regTxFreeThresh_b
Definition 3c90x.h:72
@ dnUnStall
Definition 3c90x.h:223
@ upUnStall
Definition 3c90x.h:220
@ dnStall
Definition 3c90x.h:222
@ upStall
Definition 3c90x.h:219
@ regResetMediaOptions_3_w
Definition 3c90x.h:130
@ regMaxPktSize_3_w
Definition 3c90x.h:133
@ regInternalConfig_3_l
Definition 3c90x.h:134
@ regEepromCommand_0_w
Definition 3c90x.h:150
@ regEepromData_0_w
Definition 3c90x.h:149
#define RX_BUF_SIZE
Definition 3c90x.h:269
#define INT_CMDINPROGRESS
Definition 3c90x.h:261
#define TX_RING_ALIGN
Definition 3c90x.h:267
#define EEPROM_TIMEOUT
Definition 3c90x.h:273
#define RX_RING_ALIGN
Definition 3c90x.h:268
@ eepromRead
Definition 3c90x.h:234
@ eepromRead_556
Definition 3c90x.h:235
@ eepromBusy
Definition 3c90x.h:233
@ eepromHwAddrOffset
Definition 3c90x.h:236
@ regStationMask_2_3w
Definition 3c90x.h:140
@ regStationAddress_2_3w
Definition 3c90x.h:141
@ regResetOptions_2_w
Definition 3c90x.h:139
@ downLastFrag
Definition 3c90x.h:210
@ upLastFrag
Definition 3c90x.h:209
@ globalResetMaskNetwork
Definition 3c90x.h:198
@ winAddressing2
Definition 3c90x.h:164
@ winNone
Definition 3c90x.h:158
@ winEepromBios0
Definition 3c90x.h:166
@ winTxRxOptions3
Definition 3c90x.h:163
#define INT_TXCOMPLETE
Definition 3c90x.h:253
@ fshRndupDefeat
Definition 3c90x.h:205
@ fshTxIndicate
Definition 3c90x.h:203
@ upComplete
Definition 3c90x.h:214
@ upError
Definition 3c90x.h:215
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
struct golan_eqe_cmd cmd
Definition CIB_PRM.h:1
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
struct arbelprm_completion_with_error error
Definition arbel.h:1
unsigned short uint16_t
Definition stdint.h:11
long index
Definition bigint.h:65
if(len >=6 *4) __asm__ __volatile__("movsl" if(len >=5 *4) __asm__ __volatile__("movsl" if(len >=4 *4) __asm__ __volatile__("movsl" if(len >=3 *4) __asm__ __volatile__("movsl" if(len >=2 *4) __asm__ __volatile__("movsl" if(len >=1 *4) __asm__ __volatile__("movsl" if((len % 4) >=2) __asm__ __volatile__("movsw" if((len % 2) >=1) __asm__ __volatile__("movsb" retur dest)
Definition string.h:151
Assertions.
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
static unsigned long ioaddr
Definition davicom.c:129
ring len
Length.
Definition dwmac.h:226
uint8_t data[48]
Additional event data.
Definition ena.h:11
uint8_t status
Status.
Definition ena.h:5
uint64_t address
Base address.
Definition ena.h:13
Error codes.
struct net_device * alloc_etherdev(size_t priv_size)
Allocate Ethernet device.
Definition ethernet.c:265
Ethernet protocol.
static struct net_device * netdev
Definition gdbudp.c:53
#define __unused
Declare a variable or data structure as unused.
Definition compiler.h:573
#define DBGP(...)
Definition compiler.h:532
#define DBG2(...)
Definition compiler.h:515
#define DBG(...)
Print a debugging message.
Definition compiler.h:498
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define EINVAL
Invalid argument.
Definition errno.h:429
#define ENOMEM
Not enough space.
Definition errno.h:535
#define ENOTSUP
Operation not supported.
Definition errno.h:590
#define ENOBUFS
No buffer space available.
Definition errno.h:499
#define ETH_ALEN
Definition if_ether.h:9
struct hv_monitor_parameter param[4][32]
Parameters.
Definition hyperv.h:13
iPXE I/O API
#define inw(io_addr)
Definition io.h:292
#define outb(data, io_addr)
Definition io.h:310
#define outw(data, io_addr)
Definition io.h:320
#define inl(io_addr)
Definition io.h:301
#define outl(data, io_addr)
Definition io.h:330
static __always_inline unsigned long virt_to_bus(volatile const void *addr)
Convert virtual address to a bus address.
Definition io.h:184
iPXE timers
void __asmcall int val
Definition setjmp.h:12
String functions.
void * memset(void *dest, int character, size_t len) __nonnull
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition iobuf.c:153
struct io_buffer * alloc_iob(size_t len)
Allocate I/O buffer.
Definition iobuf.c:131
I/O buffers.
#define iob_put(iobuf, len)
Definition iobuf.h:125
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition iobuf.h:160
unsigned long tmp
Definition linux_pci.h:65
void * malloc_phys(size_t size, size_t phys_align)
Allocate memory with specified physical alignment.
Definition malloc.c:707
void free_phys(void *ptr, size_t size)
Free memory allocated with malloc_phys()
Definition malloc.c:723
Dynamic memory allocation.
void netdev_rx(struct net_device *netdev, struct io_buffer *iobuf)
Add packet to receive queue.
Definition netdevice.c:549
void unregister_netdev(struct net_device *netdev)
Unregister network device.
Definition netdevice.c:942
void netdev_rx_err(struct net_device *netdev, struct io_buffer *iobuf, int rc)
Discard received packet.
Definition netdevice.c:587
int register_netdev(struct net_device *netdev)
Register network device.
Definition netdevice.c:760
Network device management.
static void netdev_link_up(struct net_device *netdev)
Mark network device as having link up.
Definition netdevice.h:789
static void netdev_init(struct net_device *netdev, struct net_device_operations *op)
Initialise a network device.
Definition netdevice.h:519
static void netdev_nullify(struct net_device *netdev)
Stop using a network device.
Definition netdevice.h:532
static void netdev_put(struct net_device *netdev)
Drop reference to network device.
Definition netdevice.h:576
static void netdev_tx_complete(struct net_device *netdev, struct io_buffer *iobuf)
Complete network transmission.
Definition netdevice.h:767
int nvs_read(struct nvs_device *nvs, unsigned int address, void *data, size_t len)
Read from non-volatile storage device.
Definition nvs.c:76
Non-volatile storage.
void adjust_pci_device(struct pci_device *pci)
Enable PCI device.
Definition pci.c:241
PCI bus.
#define __pci_driver
Declare a PCI driver.
Definition pci.h:278
static void pci_set_drvdata(struct pci_device *pci, void *priv)
Set PCI driver-private data.
Definition pci.h:366
#define PCI_ROM(_vendor, _device, _name, _description, _data)
Definition pci.h:308
static void * pci_get_drvdata(struct pci_device *pci)
Get PCI driver-private data.
Definition pci.h:376
@ cfg
Definition sis900.h:23
#define container_of(ptr, type, field)
Get containing structure.
Definition stddef.h:36
unsigned char CurrentWindow
Definition 3c90x.h:295
struct TXD * tx_ring
Definition 3c90x.h:302
struct io_buffer * tx_iobuf[TX_RING_SIZE]
Definition 3c90x.h:304
unsigned int tx_cur
Definition 3c90x.h:298
unsigned int tx_tail
Definition 3c90x.h:300
unsigned char isBrev
Definition 3c90x.h:294
struct nvs_device nvs
Definition 3c90x.h:306
unsigned int rx_cur
Definition 3c90x.h:301
unsigned int tx_cnt
Definition 3c90x.h:299
struct RXD * rx_ring
Definition 3c90x.h:303
unsigned int is3c556
Definition 3c90x.h:293
unsigned int IOAddr
Definition 3c90x.h:296
struct io_buffer * rx_iobuf[RX_RING_SIZE]
Definition 3c90x.h:305
unsigned short eeprom[0x21]
Definition 3c90x.h:297
Definition 3c90x.h:284
volatile unsigned int DataAddr
Definition 3c90x.h:287
volatile unsigned int UpPktStatus
Definition 3c90x.h:286
volatile unsigned int UpNextPtr
Definition 3c90x.h:285
volatile unsigned int DataLength
Definition 3c90x.h:288
Definition 3c90x.h:276
volatile unsigned int DataLength
Definition 3c90x.h:280
volatile unsigned int DataAddr
Definition 3c90x.h:279
volatile unsigned int FrameStartHeader
Definition 3c90x.h:278
volatile unsigned int DnNextPtr
Definition 3c90x.h:277
A persistent I/O buffer.
Definition iobuf.h:38
void * data
Start of data.
Definition iobuf.h:53
Network device operations.
Definition netdevice.h:214
A network device.
Definition netdevice.h:353
A non-volatile storage device.
Definition nvs.h:16
unsigned int block_size
Data block size (in words)
Definition nvs.h:37
unsigned int word_len_log2
Word length.
Definition nvs.h:23
int(* read)(struct nvs_device *nvs, unsigned int address, void *data, size_t len)
Read data from device.
Definition nvs.h:48
unsigned int size
Device size (in words)
Definition nvs.h:25
int(* write)(struct nvs_device *nvs, unsigned int address, const void *data, size_t len)
Write data to device.
Definition nvs.h:60
A PCI device ID list entry.
Definition pci.h:175
A PCI device.
Definition pci.h:211
unsigned long ioaddr
I/O address.
Definition pci.h:226
struct device dev
Generic device.
Definition pci.h:213
uint16_t vendor
Vendor ID.
Definition pci.h:228
uint16_t device
Device ID.
Definition pci.h:230
A PCI driver.
Definition pci.h:252
int(* probe)(struct pci_device *pci)
Probe device.
Definition pci.h:265
void udelay(unsigned long usecs)
Delay for a fixed number of microseconds.
Definition timer.c:61
static struct xen_remove_from_physmap * remove
Definition xenmem.h:40