"Fossies" - the Fresh Open Source Software Archive

Member "syslinux-6.03/gpxe/src/drivers/net/3c90x.c" (6 Oct 2014, 27485 Bytes) of package /linux/misc/syslinux-6.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * 3c90x.c -- This file implements a gPXE 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 gPXE 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 
   39 FILE_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 <gpxe/ethernet.h>
   51 #include <gpxe/if_ether.h>
   52 #include <gpxe/io.h>
   53 #include <gpxe/iobuf.h>
   54 #include <gpxe/malloc.h>
   55 #include <gpxe/netdevice.h>
   56 #include <gpxe/pci.h>
   57 #include <gpxe/timer.h>
   58 #include <gpxe/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  */
   70 static 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 */
   78     outw(val, ioaddr + regCommandIntStatus_w);
   79 
   80     /* Wait for the cmd to complete */
   81     for (cnt = 0; cnt < 100000; cnt++) {
   82         if (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) {
   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  */
   99 static 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. */
  107     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  108                      cmdSelectRegisterWindow, window);
  109     inf_3c90x->CurrentWindow = window;
  110 
  111     return;
  112 }
  113 
  114 static 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  */
  140 static int
  141 a3c90x_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 */
  153     a3c90x_internal_SetWindow(inf_3c90x, winEepromBios0);
  154 
  155     /* set eepromRead bits in command sent to NIC */
  156     address += (inf_3c90x->is3c556 ? eepromRead_556 : eepromRead);
  157 
  158     a3c90x_internal_WaitForEeprom(inf_3c90x);
  159     /* send address to NIC */
  160     outw(address, inf_3c90x->IOAddr + regEepromCommand_0_w);
  161     a3c90x_internal_WaitForEeprom(inf_3c90x);
  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  */
  178 static int
  179 a3c90x_internal_WriteEeprom(struct nvs_device *nvs __unused,
  180                 unsigned int address __unused,
  181                 const void *data __unused, size_t len __unused)
  182 {
  183     return -ENOTSUP;
  184 }
  185 
  186 static 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  */
  204 static void a3c90x_reset(struct INF_3C90X *inf_3c90x)
  205 {
  206     DBGP("a3c90x_reset\n");
  207     /* Send the reset command to the card */
  208     DBG("3c90x: Issuing RESET\n");
  209     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdGlobalReset, 0);
  210 
  211     /* global reset command resets station mask, non-B revision cards
  212      * require explicit reset of values
  213      */
  214     a3c90x_internal_SetWindow(inf_3c90x, winAddressing2);
  215     outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 0);
  216     outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 2);
  217     outw(0, inf_3c90x->IOAddr + regStationMask_2_3w + 4);
  218 
  219     /* Issue transmit reset, wait for command completion */
  220     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxReset, 0);
  221 
  222     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0);
  223 
  224     /*
  225      * reset of the receiver on B-revision cards re-negotiates the link
  226      * takes several seconds (a computer eternity)
  227      */
  228     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxReset,
  229                      inf_3c90x->isBrev ? 0x04 : 0x00);
  230 
  231     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0);
  232 
  233     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  234                      cmdSetInterruptEnable, 0);
  235     /* enable rxComplete and txComplete */
  236     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  237                      cmdSetIndicationEnable,
  238                      INT_TXCOMPLETE | INT_UPCOMPLETE);
  239     /* acknowledge any pending status flags */
  240     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  241                      cmdAcknowledgeInterrupt, 0x661);
  242 
  243     return;
  244 }
  245 
  246 /**
  247  * a3c90x_setup_tx_ring - Allocates TX ring, initialize tx_desc values
  248  *
  249  * @v p Private NIC data
  250  *
  251  * @ret Returns 0 on success, negative on failure
  252  */
  253 static int a3c90x_setup_tx_ring(struct INF_3C90X *p)
  254 {
  255     DBGP("a3c90x_setup_tx_ring\n");
  256     p->tx_ring =
  257         malloc_dma(TX_RING_SIZE * sizeof(struct TXD), TX_RING_ALIGN);
  258 
  259     if (!p->tx_ring) {
  260         DBG("Could not allocate TX-ring\n");
  261         return -ENOMEM;
  262     }
  263 
  264     memset(p->tx_ring, 0, TX_RING_SIZE * sizeof(struct TXD));
  265     p->tx_cur = 0;
  266     p->tx_cnt = 0;
  267     p->tx_tail = 0;
  268 
  269     return 0;
  270 }
  271 
  272 /**
  273  * a3c90x_process_tx_packets - Checks for successfully sent packets,
  274  * reports them to gPXE with netdev_tx_complete();
  275  *
  276  * @v netdev    Network device info
  277  */
  278 static void a3c90x_process_tx_packets(struct net_device *netdev)
  279 {
  280     struct INF_3C90X *p = netdev_priv(netdev);
  281     unsigned int downlist_ptr;
  282 
  283     DBGP("a3c90x_process_tx_packets\n");
  284 
  285     DBG("    tx_cnt: %d\n", p->tx_cnt);
  286 
  287     while (p->tx_tail != p->tx_cur) {
  288 
  289         downlist_ptr = inl(p->IOAddr + regDnListPtr_l);
  290 
  291         DBG("    downlist_ptr: %#08x\n", downlist_ptr);
  292         DBG("    tx_tail: %d tx_cur: %d\n", p->tx_tail, p->tx_cur);
  293 
  294         /* NIC is currently working on this tx desc */
  295         if(downlist_ptr == virt_to_bus(p->tx_ring + p->tx_tail))
  296             return;
  297 
  298         netdev_tx_complete(netdev, p->tx_iobuf[p->tx_tail]);
  299 
  300         DBG("transmitted packet\n");
  301         DBG("    size: %zd\n", iob_len(p->tx_iobuf[p->tx_tail]));
  302 
  303         p->tx_tail = (p->tx_tail + 1) % TX_RING_SIZE;
  304         p->tx_cnt--;
  305     }
  306 }
  307 
  308 static void a3c90x_free_tx_ring(struct INF_3C90X *p)
  309 {
  310     DBGP("a3c90x_free_tx_ring\n");
  311 
  312     free_dma(p->tx_ring, TX_RING_SIZE * sizeof(struct TXD));
  313     p->tx_ring = NULL;
  314     /* io_buffers are free()ed by netdev_tx_complete[,_err]() */
  315 }
  316 
  317 /**
  318  * a3c90x_transmit - Transmits a packet.
  319  *
  320  * @v netdev    Network device info
  321  * @v iob       io_buffer containing the data to be send
  322  *
  323  * @ret Returns 0 on success, negative on failure
  324  */
  325 static int a3c90x_transmit(struct net_device *netdev,
  326                struct io_buffer *iob)
  327 {
  328     struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
  329     struct TXD *tx_cur_desc;
  330     struct TXD *tx_prev_desc;
  331 
  332     unsigned int len;
  333     unsigned int downlist_ptr;
  334 
  335     DBGP("a3c90x_transmit\n");
  336 
  337     if (inf_3c90x->tx_cnt == TX_RING_SIZE) {
  338         DBG("TX-Ring overflow\n");
  339         return -ENOBUFS;
  340     }
  341 
  342     inf_3c90x->tx_iobuf[inf_3c90x->tx_cur] = iob;
  343     tx_cur_desc = inf_3c90x->tx_ring + inf_3c90x->tx_cur;
  344 
  345     tx_prev_desc = inf_3c90x->tx_ring +
  346         (((inf_3c90x->tx_cur + TX_RING_SIZE) - 1) % TX_RING_SIZE);
  347 
  348     len = iob_len(iob);
  349 
  350     /* Setup the DPD (download descriptor) */
  351     tx_cur_desc->DnNextPtr = 0;
  352 
  353     /* FrameStartHeader differs in 90x and >= 90xB
  354      * It contains length in 90x and a round up boundary and packet ID for
  355      * 90xB and 90xC. We can leave this to 0 for 90xB and 90xC.
  356      */
  357     tx_cur_desc->FrameStartHeader =
  358         fshTxIndicate | (inf_3c90x->isBrev ? 0x00 : len);
  359 
  360     tx_cur_desc->DataAddr = virt_to_bus(iob->data);
  361     tx_cur_desc->DataLength = len | downLastFrag;
  362 
  363     /* We have to stall the download engine, so the NIC won't access the
  364      * tx descriptor while we modify it. There is a way around this
  365      * from revision B and upwards. To stay compatible with older revisions
  366      * we don't use it here.
  367      */
  368     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl,
  369                      dnStall);
  370 
  371     tx_prev_desc->DnNextPtr = virt_to_bus(tx_cur_desc);
  372 
  373     downlist_ptr = inl(inf_3c90x->IOAddr + regDnListPtr_l);
  374     if (downlist_ptr == 0) {
  375         /* currently no DownList, sending a new one */
  376         outl(virt_to_bus(tx_cur_desc),
  377              inf_3c90x->IOAddr + regDnListPtr_l);
  378     }
  379 
  380     /* End Stall */
  381     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdStallCtl,
  382                      dnUnStall);
  383 
  384     inf_3c90x->tx_cur = (inf_3c90x->tx_cur + 1) % TX_RING_SIZE;
  385     inf_3c90x->tx_cnt++;
  386 
  387     return 0;
  388 }
  389 
  390 /**
  391  * a3c90x_prepare_rx_desc - fills the rx desc with initial data
  392  *
  393  * @v p     NIC private data
  394  * @v index Index for rx_iobuf and rx_ring array
  395  */
  396 
  397 static void a3c90x_prepare_rx_desc(struct INF_3C90X *p, unsigned int index)
  398 {
  399     DBGP("a3c90x_prepare_rx_desc\n");
  400     DBG("Populating rx_desc %d\n", index);
  401 
  402     /* We have to stall the upload engine, so the NIC won't access the
  403      * rx descriptor while we modify it. There is a way around this
  404      * from revision B and upwards. To stay compatible with older revisions
  405      * we don't use it here.
  406      */
  407     a3c90x_internal_IssueCommand(p->IOAddr, cmdStallCtl, upStall);
  408 
  409     p->rx_ring[index].DataAddr = virt_to_bus(p->rx_iobuf[index]->data);
  410     p->rx_ring[index].DataLength = RX_BUF_SIZE | upLastFrag;
  411     p->rx_ring[index].UpPktStatus = 0;
  412 
  413     /* unstall upload engine */
  414     a3c90x_internal_IssueCommand(p->IOAddr, cmdStallCtl, upUnStall);
  415 }
  416 
  417 /**
  418  * a3c90x_refill_rx_ring -checks every entry in the rx ring and reallocates
  419  * them as necessary. Then it calls a3c90x_prepare_rx_desc to fill the rx desc
  420  * with initial data.
  421  *
  422  * @v p     NIC private data
  423  */
  424 static void a3c90x_refill_rx_ring(struct INF_3C90X *p)
  425 {
  426     int i;
  427     unsigned int status;
  428     struct RXD *rx_cur_desc;
  429 
  430     DBGP("a3c90x_refill_rx_ring\n");
  431 
  432     for (i = 0; i < RX_RING_SIZE; i++) {
  433         rx_cur_desc = p->rx_ring + i;
  434         status = rx_cur_desc->UpPktStatus;
  435 
  436         /* only refill used descriptor */
  437         if (!(status & upComplete))
  438             continue;
  439 
  440         /* we still need to process this descriptor */
  441         if (p->rx_iobuf[i] != NULL)
  442             continue;
  443 
  444         p->rx_iobuf[i] = alloc_iob(RX_BUF_SIZE);
  445         if (p->rx_iobuf[i] == NULL) {
  446             DBG("alloc_iob() failed\n");
  447             break;
  448         }
  449 
  450         a3c90x_prepare_rx_desc(p, i);
  451     }
  452 }
  453 
  454 /**
  455  * a3c90x_setup_rx_ring - Allocates RX ring, initialize rx_desc values
  456  *
  457  * @v p Private NIC data
  458  *
  459  * @ret Returns 0 on success, negative on failure
  460  */
  461 static int a3c90x_setup_rx_ring(struct INF_3C90X *p)
  462 {
  463     int i;
  464 
  465     DBGP("a3c90x_setup_rx_ring\n");
  466 
  467     p->rx_ring =
  468         malloc_dma(RX_RING_SIZE * sizeof(struct RXD), RX_RING_ALIGN);
  469 
  470     if (!p->rx_ring) {
  471         DBG("Could not allocate RX-ring\n");
  472         return -ENOMEM;
  473     }
  474 
  475     p->rx_cur = 0;
  476 
  477     for (i = 0; i < RX_RING_SIZE; i++) {
  478         p->rx_ring[i].UpNextPtr =
  479             virt_to_bus(p->rx_ring + (i + 1));
  480 
  481         /* these are needed so refill_rx_ring initializes the ring */
  482         p->rx_ring[i].UpPktStatus = upComplete;
  483         p->rx_iobuf[i] = NULL;
  484     }
  485 
  486     /* Loop the ring */
  487     p->rx_ring[i - 1].UpNextPtr = virt_to_bus(p->rx_ring);
  488 
  489     a3c90x_refill_rx_ring(p);
  490 
  491     return 0;
  492 }
  493 
  494 static void a3c90x_free_rx_ring(struct INF_3C90X *p)
  495 {
  496     DBGP("a3c90x_free_rx_ring\n");
  497 
  498     free_dma(p->rx_ring, RX_RING_SIZE * sizeof(struct RXD));
  499     p->rx_ring = NULL;
  500 }
  501 
  502 static void a3c90x_free_rx_iobuf(struct INF_3C90X *p)
  503 {
  504     int i;
  505 
  506     DBGP("a3c90x_free_rx_iobuf\n");
  507 
  508     for (i = 0; i < RX_RING_SIZE; i++) {
  509         free_iob(p->rx_iobuf[i]);
  510         p->rx_iobuf[i] = NULL;
  511     }
  512 }
  513 
  514 /**
  515  * a3c90x_process_rx_packets - Checks for received packets,
  516  * reports them to gPXE with netdev_rx() or netdev_rx_err() if there was an
  517  * error while receiving the packet
  518  *
  519  * @v netdev    Network device info
  520  */
  521 static void a3c90x_process_rx_packets(struct net_device *netdev)
  522 {
  523     int i;
  524     unsigned int rx_status;
  525     struct INF_3C90X *p = netdev_priv(netdev);
  526     struct RXD *rx_cur_desc;
  527 
  528     DBGP("a3c90x_process_rx_packets\n");
  529 
  530     for (i = 0; i < RX_RING_SIZE; i++) {
  531         rx_cur_desc = p->rx_ring + p->rx_cur;
  532         rx_status = rx_cur_desc->UpPktStatus;
  533 
  534         if (!(rx_status & upComplete) && !(rx_status & upError))
  535             break;
  536 
  537         if (p->rx_iobuf[p->rx_cur] == NULL)
  538             break;
  539 
  540         if (rx_status & upError) {
  541             DBG("Corrupted packet received\n");
  542             netdev_rx_err(netdev, p->rx_iobuf[p->rx_cur],
  543                       -EINVAL);
  544         } else {
  545             /* if we're here, we've got good packet */
  546             int packet_len;
  547 
  548             packet_len = rx_status & 0x1FFF;
  549             iob_put(p->rx_iobuf[p->rx_cur], packet_len);
  550 
  551             DBG("received packet\n");
  552             DBG("    size: %d\n", packet_len);
  553 
  554             netdev_rx(netdev, p->rx_iobuf[p->rx_cur]);
  555         }
  556 
  557         p->rx_iobuf[p->rx_cur] = NULL;  /* invalidate rx desc */
  558         p->rx_cur = (p->rx_cur + 1) % RX_RING_SIZE;
  559     }
  560     a3c90x_refill_rx_ring(p);
  561 
  562 }
  563 
  564 /**
  565  * a3c90x_poll - Routine that gets called periodically.
  566  * Here we hanle transmitted and received packets.
  567  * We could also check the link status from time to time, which we
  568  * currently don't do.
  569  *
  570  * @v netdev    Network device info
  571  */
  572 static void a3c90x_poll(struct net_device *netdev)
  573 {
  574     struct INF_3C90X *p = netdev_priv(netdev);
  575     uint16_t raw_status, int_status;
  576 
  577     DBGP("a3c90x_poll\n");
  578 
  579     raw_status = inw(p->IOAddr + regCommandIntStatus_w);
  580     int_status = (raw_status & 0x0FFF);
  581 
  582     if ( int_status == 0 )
  583         return;
  584 
  585     a3c90x_internal_IssueCommand(p->IOAddr, cmdAcknowledgeInterrupt,
  586                      int_status);
  587 
  588     if (int_status & INT_TXCOMPLETE)
  589         outb(0x00, p->IOAddr + regTxStatus_b);
  590 
  591     DBG("poll: status = %#04x\n", raw_status);
  592 
  593     a3c90x_process_tx_packets(netdev);
  594 
  595     a3c90x_process_rx_packets(netdev);
  596 }
  597 
  598 
  599 
  600 static void a3c90x_free_resources(struct INF_3C90X *p)
  601 {
  602     DBGP("a3c90x_free_resources\n");
  603 
  604     a3c90x_free_tx_ring(p);
  605     a3c90x_free_rx_ring(p);
  606     a3c90x_free_rx_iobuf(p);
  607 }
  608 
  609 /**
  610  * a3c90x_remove - Routine to remove the card. Unregisters
  611  * the NIC from gPXE, disables RX/TX and resets the card.
  612  *
  613  * @v pci   PCI device info
  614  */
  615 static void a3c90x_remove(struct pci_device *pci)
  616 {
  617     struct net_device *netdev = pci_get_drvdata(pci);
  618     struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
  619 
  620     DBGP("a3c90x_remove\n");
  621 
  622     a3c90x_reset(inf_3c90x);
  623 
  624     /* Disable the receiver and transmitter. */
  625     outw(cmdRxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
  626     outw(cmdTxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
  627 
  628     unregister_netdev(netdev);
  629     netdev_nullify(netdev);
  630     netdev_put(netdev);
  631 }
  632 
  633 static void a3c90x_irq(struct net_device *netdev, int enable)
  634 {
  635     struct INF_3C90X *p = netdev_priv(netdev);
  636 
  637     DBGP("a3c90x_irq\n");
  638 
  639     if (enable == 0) {
  640         /* disable interrupts */
  641         a3c90x_internal_IssueCommand(p->IOAddr,
  642                          cmdSetInterruptEnable, 0);
  643     } else {
  644         a3c90x_internal_IssueCommand(p->IOAddr,
  645                          cmdSetInterruptEnable,
  646                          INT_TXCOMPLETE |
  647                          INT_UPCOMPLETE);
  648         a3c90x_internal_IssueCommand(p->IOAddr,
  649                          cmdAcknowledgeInterrupt,
  650                          0x661);
  651     }
  652 }
  653 
  654 /**
  655  * a3c90x_hw_start - Initialize hardware, copy MAC address
  656  * to NIC registers, set default receiver
  657  */
  658 static void a3c90x_hw_start(struct net_device *netdev)
  659 {
  660     int i, c;
  661     unsigned int cfg;
  662     unsigned int mopt;
  663     unsigned short linktype;
  664     struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
  665 
  666     DBGP("a3c90x_hw_start\n");
  667 
  668     /* 3C556: Invert MII power */
  669     if (inf_3c90x->is3c556) {
  670         unsigned int tmp;
  671         a3c90x_internal_SetWindow(inf_3c90x, winAddressing2);
  672         tmp = inw(inf_3c90x->IOAddr + regResetOptions_2_w);
  673         tmp |= 0x4000;
  674         outw(tmp, inf_3c90x->IOAddr + regResetOptions_2_w);
  675     }
  676 
  677     /* Copy MAC address into the NIC registers */
  678     a3c90x_internal_SetWindow(inf_3c90x, winAddressing2);
  679     for (i = 0; i < ETH_ALEN; i++)
  680         outb(netdev->ll_addr[i],
  681              inf_3c90x->IOAddr + regStationAddress_2_3w + i);
  682     for (i = 0; i < ETH_ALEN; i++)
  683         outb(0, inf_3c90x->IOAddr + regStationMask_2_3w + i);
  684 
  685     /* Read the media options register, print a message and set default
  686     * xcvr.
  687     *
  688     * Uses Media Option command on B revision, Reset Option on non-B
  689     * revision cards -- same register address
  690     */
  691     a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3);
  692     mopt = inw(inf_3c90x->IOAddr + regResetMediaOptions_3_w);
  693 
  694     /* mask out VCO bit that is defined as 10baseFL bit on B-rev cards */
  695     if (!inf_3c90x->isBrev) {
  696         mopt &= 0x7F;
  697     }
  698 
  699     DBG("Connectors present: ");
  700     c = 0;
  701     linktype = 0x0008;
  702     if (mopt & 0x01) {
  703         DBG("%s100Base-T4", (c++) ? ", " : "");
  704         linktype = linkMII;
  705     }
  706     if (mopt & 0x04) {
  707         DBG("%s100Base-FX", (c++) ? ", " : "");
  708         linktype = link100BaseFX;
  709     }
  710     if (mopt & 0x10) {
  711         DBG("%s10Base-2", (c++) ? ", " : "");
  712         linktype = link10Base2;
  713     }
  714     if (mopt & 0x20) {
  715         DBG("%sAUI", (c++) ? ", " : "");
  716         linktype = linkAUI;
  717     }
  718     if (mopt & 0x40) {
  719         DBG("%sMII", (c++) ? ", " : "");
  720         linktype = linkMII;
  721     }
  722     if ((mopt & 0xA) == 0xA) {
  723         DBG("%s10Base-T / 100Base-TX", (c++) ? ", " : "");
  724         linktype = linkAutoneg;
  725     } else if ((mopt & 0xA) == 0x2) {
  726         DBG("%s100Base-TX", (c++) ? ", " : "");
  727         linktype = linkAutoneg;
  728     } else if ((mopt & 0xA) == 0x8) {
  729         DBG("%s10Base-T", (c++) ? ", " : "");
  730         linktype = linkAutoneg;
  731     }
  732     DBG(".\n");
  733 
  734     /* Determine transceiver type to use, depending on value stored in
  735     * eeprom 0x16
  736     */
  737     if (inf_3c90x->isBrev) {
  738         if ((inf_3c90x->eeprom[0x16] & 0xFF00) == XCVR_MAGIC) {
  739             /* User-defined */
  740             linktype = inf_3c90x->eeprom[0x16] & 0x000F;
  741         }
  742     } else {
  743         /* I don't know what MII MAC only mode is!!! */
  744         if (linktype == linkExternalMII) {
  745             if (inf_3c90x->isBrev)
  746                 DBG("WARNING: MII External MAC Mode only supported on B-revision " "cards!!!!\nFalling Back to MII Mode\n");
  747             linktype = linkMII;
  748         }
  749     }
  750 
  751     /* enable DC converter for 10-Base-T */
  752     if (linktype == link10Base2) {
  753         a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  754                          cmdEnableDcConverter, 0);
  755     }
  756 
  757     /* Set the link to the type we just determined. */
  758     a3c90x_internal_SetWindow(inf_3c90x, winTxRxOptions3);
  759     cfg = inl(inf_3c90x->IOAddr + regInternalConfig_3_l);
  760     cfg &= ~(0xF << 20);
  761     cfg |= (linktype << 20);
  762 
  763     DBG("Setting internal cfg register: 0x%08X (linktype: 0x%02X)\n",
  764         cfg, linktype);
  765 
  766     outl(cfg, inf_3c90x->IOAddr + regInternalConfig_3_l);
  767 
  768     /* Now that we set the xcvr type, reset the Tx and Rx */
  769     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxReset, 0x00);
  770 
  771     if (!inf_3c90x->isBrev)
  772         outb(0x01, inf_3c90x->IOAddr + regTxFreeThresh_b);
  773 
  774     /* Set the RX filter = receive only individual pkts & multicast & bcast. */
  775     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdSetRxFilter,
  776                      0x01 + 0x02 + 0x04);
  777 
  778 
  779     /*
  780     * set Indication and Interrupt flags , acknowledge any IRQ's
  781     */
  782     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  783                      cmdSetInterruptEnable,
  784      INT_TXCOMPLETE | INT_UPCOMPLETE);
  785     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  786                      cmdSetIndicationEnable,
  787      INT_TXCOMPLETE | INT_UPCOMPLETE);
  788     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr,
  789                      cmdAcknowledgeInterrupt, 0x661);
  790 }
  791 
  792 /**
  793  * a3c90x_open - Routine to initialize the card. Initialize hardware,
  794  * allocate TX and RX ring, send RX ring address to the NIC.
  795  *
  796  * @v netdev    Network device info
  797  *
  798  * @ret Returns 0 on success, negative on failure
  799  */
  800 static int a3c90x_open(struct net_device *netdev)
  801 {
  802     int rc;
  803     struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
  804 
  805     DBGP("a3c90x_open\n");
  806 
  807     a3c90x_hw_start(netdev);
  808 
  809     rc = a3c90x_setup_tx_ring(inf_3c90x);
  810     if (rc != 0) {
  811         DBG("Error setting up TX Ring\n");
  812         goto error;
  813     }
  814 
  815     rc = a3c90x_setup_rx_ring(inf_3c90x);
  816     if (rc != 0) {
  817         DBG("Error setting up RX Ring\n");
  818         goto error;
  819     }
  820 
  821     /* send rx_ring address to NIC */
  822     outl(virt_to_bus(inf_3c90x->rx_ring),
  823          inf_3c90x->IOAddr + regUpListPtr_l);
  824 
  825     /* enable packet transmission and reception */
  826     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdTxEnable, 0);
  827     a3c90x_internal_IssueCommand(inf_3c90x->IOAddr, cmdRxEnable, 0);
  828 
  829     return 0;
  830 
  831       error:
  832     a3c90x_free_resources(inf_3c90x);
  833     a3c90x_reset(inf_3c90x);
  834     return rc;
  835 }
  836 
  837 /**
  838  * a3c90x_close - free()s TX and RX ring, disablex RX/TX, resets NIC
  839  *
  840  * @v netdev    Network device info
  841  */
  842 static void a3c90x_close(struct net_device *netdev)
  843 {
  844     struct INF_3C90X *inf_3c90x = netdev_priv(netdev);
  845 
  846     DBGP("a3c90x_close\n");
  847 
  848     a3c90x_reset(inf_3c90x);
  849     outw(cmdRxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
  850     outw(cmdTxDisable, inf_3c90x->IOAddr + regCommandIntStatus_w);
  851     a3c90x_free_resources(inf_3c90x);
  852 }
  853 
  854 static struct net_device_operations a3c90x_operations = {
  855     .open = a3c90x_open,
  856     .close = a3c90x_close,
  857     .poll = a3c90x_poll,
  858     .transmit = a3c90x_transmit,
  859     .irq = a3c90x_irq,
  860 };
  861 
  862 /**
  863  * a3c90x_probe: exported routine to probe for the 3c905 card.
  864  * If this routine is called, the pci functions did find the
  865  * card.  We read the eeprom here and get the MAC address.
  866  * Initialization is done in a3c90x_open().
  867  *
  868  * @v pci   PCI device info
  869  * @ pci_id PCI device IDs
  870  *
  871  * @ret rc  Returns 0 on success, negative on failure
  872  */
  873 static int a3c90x_probe(struct pci_device *pci,
  874             const struct pci_device_id *pci_id __unused)
  875 {
  876 
  877     struct net_device *netdev;
  878     struct INF_3C90X *inf_3c90x;
  879     unsigned char *HWAddr;
  880     int rc;
  881 
  882     DBGP("a3c90x_probe\n");
  883 
  884     if (pci->ioaddr == 0)
  885         return -EINVAL;
  886 
  887     netdev = alloc_etherdev(sizeof(*inf_3c90x));
  888     if (!netdev)
  889         return -ENOMEM;
  890 
  891     netdev_init(netdev, &a3c90x_operations);
  892     pci_set_drvdata(pci, netdev);
  893     netdev->dev = &pci->dev;
  894 
  895     inf_3c90x = netdev_priv(netdev);
  896     memset(inf_3c90x, 0, sizeof(*inf_3c90x));
  897 
  898     adjust_pci_device(pci);
  899 
  900     inf_3c90x->is3c556 = (pci->device == 0x6055);
  901     inf_3c90x->IOAddr = pci->ioaddr;
  902     inf_3c90x->CurrentWindow = winNone;
  903 
  904     inf_3c90x->isBrev = 1;
  905     switch (pci->device) {
  906     case 0x9000:        /* 10 Base TPO             */
  907     case 0x9001:        /* 10/100 T4               */
  908     case 0x9050:        /* 10/100 TPO              */
  909     case 0x9051:        /* 10 Base Combo           */
  910         inf_3c90x->isBrev = 0;
  911         break;
  912     }
  913 
  914     DBG("[3c90x]: found NIC(0x%04X, 0x%04X), isBrev=%d, is3c556=%d\n",
  915         pci->vendor, pci->device, inf_3c90x->isBrev,
  916         inf_3c90x->is3c556);
  917 
  918     /* initialize nvs device */
  919     inf_3c90x->nvs.word_len_log2 = 1;   /* word */
  920     inf_3c90x->nvs.size = (inf_3c90x->isBrev ? 0x20 : 0x17);
  921     inf_3c90x->nvs.block_size = 1;
  922     inf_3c90x->nvs.read = a3c90x_internal_ReadEeprom;
  923     inf_3c90x->nvs.write = a3c90x_internal_WriteEeprom;
  924 
  925     /* reset NIC before accessing any data from it */
  926     a3c90x_reset(inf_3c90x);
  927 
  928     /* load eeprom contents to inf_3c90x->eeprom */
  929     a3c90x_internal_ReadEepromContents(inf_3c90x);
  930 
  931     HWAddr = netdev->hw_addr;
  932 
  933     /* Retrieve the Hardware address */
  934     HWAddr[0] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] >> 8;
  935     HWAddr[1] = inf_3c90x->eeprom[eepromHwAddrOffset + 0] & 0xFF;
  936     HWAddr[2] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] >> 8;
  937     HWAddr[3] = inf_3c90x->eeprom[eepromHwAddrOffset + 1] & 0xFF;
  938     HWAddr[4] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] >> 8;
  939     HWAddr[5] = inf_3c90x->eeprom[eepromHwAddrOffset + 2] & 0xFF;
  940 
  941     /* we don't handle linkstates yet, so we're always up */
  942     netdev_link_up(netdev);
  943 
  944     if ((rc = register_netdev(netdev)) != 0) {
  945         DBG("3c90x: register_netdev() failed\n");
  946         netdev_put(netdev);
  947         return rc;
  948     }
  949 
  950     return 0;
  951 }
  952 
  953 static struct pci_device_id a3c90x_nics[] = {
  954 /* Original 90x revisions: */
  955     PCI_ROM(0x10b7, 0x6055, "3c556", "3C556", 0),   /* Huricane */
  956     PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO", 0), /* 10 Base TPO */
  957     PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo", 0),    /* 10/100 T4 */
  958     PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX", 0),   /* 100 Base TX / 10/100 TPO */
  959     PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4", 0),    /* 100 Base T4 / 10 Base Combo */
  960 /* Newer 90xB revisions: */
  961     PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO", 0),   /* 10 Base TPO */
  962     PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo", 0),   /* 10 Base Combo */
  963     PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T", 0),  /* 10 Base TP and Base2 */
  964     PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL", 0), /* 10 Base FL */
  965     PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX", 0), /* 10/100 TPO */
  966     PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4", 0), /* 10/100 T4 */
  967     PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058", 0), /* Cyclone 10/100/BNC */
  968     PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL", 0), /* 100 Base FX / 10 Base FX */
  969 /* Newer 90xC revision: */
  970     PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM", 0),   /* 10/100 TPO (3C905C-TXM) */
  971     PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)", 0),   /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
  972     PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm", "3Com20B-EMB WNM", 0),
  973     PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone", 0), /* Cyclone */
  974     PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805", 0),   /* Dual Port Server Cyclone */
  975     PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX", 0), /* Hurricane */
  976     PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado", 0),
  977     PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A", 0),
  978     PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B", 0),
  979 };
  980 
  981 struct pci_driver a3c90x_driver __pci_driver = {
  982     .ids = a3c90x_nics,
  983     .id_count = (sizeof(a3c90x_nics) / sizeof(a3c90x_nics[0])),
  984     .probe = a3c90x_probe,
  985     .remove = a3c90x_remove,
  986 };
  987 
  988 /*
  989  * Local variables:
  990  *  c-basic-offset: 8
  991  *  c-indent-level: 8
  992  *  tab-width: 8
  993  * End:
  994  */