Annotation of qemu/roms/ipxe/src/drivers/net/3c90x.c, revision 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.