Annotation of qemu/roms/ipxe/src/drivers/net/3c90x.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.