Annotation of qemu/roms/ipxe/src/drivers/net/b44.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 2008 Stefan Hajnoczi <[email protected]>
        !             3:  * Copyright (c) 2008 Pantelis Koukousoulas <[email protected]>
        !             4:  *
        !             5:  * This program is free software; you can redistribute it and/or
        !             6:  * modify it under the terms of the GNU General Public License as
        !             7:  * published by the Free Software Foundation; either version 2 of the
        !             8:  * License, or any later version.
        !             9:  *
        !            10:  * This program is distributed in the hope that it will be useful, but
        !            11:  * WITHOUT ANY WARRANTY; without even the implied warranty of
        !            12:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        !            13:  * General Public License for more details.
        !            14:  *
        !            15:  * You should have received a copy of the GNU General Public License
        !            16:  * along with this program; if not, write to the Free Software
        !            17:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
        !            18:  *
        !            19:  * This driver is a port of the b44 linux driver version 1.01
        !            20:  *
        !            21:  * Copyright (c) 2002 David S. Miller <[email protected]>
        !            22:  * Copyright (c) Pekka Pietikainen <[email protected]>
        !            23:  * Copyright (C) 2006 Broadcom Corporation.
        !            24:  *
        !            25:  * Some ssb bits copied from version 2.0 of the b44 driver
        !            26:  * Copyright (c) Michael Buesch
        !            27:  *
        !            28:  * Copyright (c) a lot of people too. Please respect their work.
        !            29:  */
        !            30: 
        !            31: FILE_LICENCE ( GPL2_OR_LATER );
        !            32: 
        !            33: #include <errno.h>
        !            34: #include <assert.h>
        !            35: #include <stdio.h>
        !            36: #include <unistd.h>
        !            37: #include <byteswap.h>
        !            38: #include <ipxe/io.h>
        !            39: #include <mii.h>
        !            40: #include <ipxe/iobuf.h>
        !            41: #include <ipxe/malloc.h>
        !            42: #include <ipxe/pci.h>
        !            43: #include <ipxe/netdevice.h>
        !            44: #include <ipxe/ethernet.h>
        !            45: #include <ipxe/if_ether.h>
        !            46: #include "b44.h"
        !            47: 
        !            48: 
        !            49: static inline int ring_next(int index)
        !            50: {
        !            51:        /* B44_RING_SIZE is a power of 2 :) */
        !            52:        return (index + 1) & (B44_RING_SIZE - 1);
        !            53: }
        !            54: 
        !            55: 
        !            56: /* Memory-mapped I/O wrappers */
        !            57: 
        !            58: static inline u32 br32(const struct b44_private *bp, u32 reg)
        !            59: {
        !            60:        return readl(bp->regs + reg);
        !            61: }
        !            62: 
        !            63: 
        !            64: static inline void bw32(const struct b44_private *bp, u32 reg, u32 val)
        !            65: {
        !            66:        writel(val, bp->regs + reg);
        !            67: }
        !            68: 
        !            69: 
        !            70: static inline void bflush(const struct b44_private *bp, u32 reg, u32 timeout)
        !            71: {
        !            72:        readl(bp->regs + reg);
        !            73:        udelay(timeout);
        !            74: }
        !            75: 
        !            76: 
        !            77: #define VIRT_TO_B44(addr)      ( virt_to_bus(addr) + SB_PCI_DMA )
        !            78: 
        !            79: 
        !            80: /**
        !            81:  * Return non-zero if the installed RAM is within
        !            82:  * the limit given and zero if it is outside.
        !            83:  * Hopefully will be removed soon.
        !            84:  */
        !            85: int phys_ram_within_limit(u64 limit)
        !            86: {
        !            87:        struct memory_map memmap;
        !            88:        struct memory_region *highest = NULL;
        !            89:        get_memmap(&memmap);
        !            90: 
        !            91:        if (memmap.count == 0)
        !            92:                return 0;
        !            93:        highest = &memmap.regions[memmap.count - 1];
        !            94: 
        !            95:        return (highest->end < limit);
        !            96: }
        !            97: 
        !            98: 
        !            99: /**
        !           100:  * Ring cells waiting to be processed are between 'tx_cur' and 'pending'
        !           101:  * indexes in the ring.
        !           102:  */
        !           103: static u32 pending_tx_index(struct b44_private *bp)
        !           104: {
        !           105:        u32 pending = br32(bp, B44_DMATX_STAT);
        !           106:        pending &= DMATX_STAT_CDMASK;
        !           107: 
        !           108:        pending /= sizeof(struct dma_desc);
        !           109:        return pending & (B44_RING_SIZE - 1);
        !           110: }
        !           111: 
        !           112: 
        !           113: /**
        !           114:  * Ring cells waiting to be processed are between 'rx_cur' and 'pending'
        !           115:  * indexes in the ring.
        !           116:  */
        !           117: static u32 pending_rx_index(struct b44_private *bp)
        !           118: {
        !           119:        u32 pending = br32(bp, B44_DMARX_STAT);
        !           120:        pending &= DMARX_STAT_CDMASK;
        !           121: 
        !           122:        pending /= sizeof(struct dma_desc);
        !           123:        return pending & (B44_RING_SIZE - 1);
        !           124: }
        !           125: 
        !           126: 
        !           127: /**
        !           128:  * Wait until the given bit is set/cleared.
        !           129:  */
        !           130: static int b44_wait_bit(struct b44_private *bp, unsigned long reg, u32 bit,
        !           131:                                    unsigned long timeout, const int clear)
        !           132: {
        !           133:        unsigned long i;
        !           134: 
        !           135:        for (i = 0; i < timeout; i++) {
        !           136:                u32 val = br32(bp, reg);
        !           137: 
        !           138:                if (clear && !(val & bit))
        !           139:                        break;
        !           140: 
        !           141:                if (!clear && (val & bit))
        !           142:                        break;
        !           143: 
        !           144:                udelay(10);
        !           145:        }
        !           146:        if (i == timeout) {
        !           147:                return -ENODEV;
        !           148:        }
        !           149:        return 0;
        !           150: }
        !           151: 
        !           152: 
        !           153: /*
        !           154:  * Sonics Silicon Backplane support. SSB is a mini-bus interconnecting
        !           155:  * so-called IP Cores. One of those cores implements the Fast Ethernet
        !           156:  * functionality and another one the PCI engine.
        !           157:  *
        !           158:  * You need to switch to the core you want to talk to before actually
        !           159:  * sending commands.
        !           160:  *
        !           161:  * See: http://bcm-v4.sipsolutions.net/Backplane for (reverse-engineered)
        !           162:  * specs.
        !           163:  */
        !           164: 
        !           165: static inline u32 ssb_get_core_rev(struct b44_private *bp)
        !           166: {
        !           167:        return (br32(bp, B44_SBIDHIGH) & SBIDHIGH_RC_MASK);
        !           168: }
        !           169: 
        !           170: 
        !           171: static inline int ssb_is_core_up(struct b44_private *bp)
        !           172: {
        !           173:        return ((br32(bp, B44_SBTMSLOW) & (SSB_CORE_DOWN | SBTMSLOW_CLOCK))
        !           174:                                                        == SBTMSLOW_CLOCK);
        !           175: }
        !           176: 
        !           177: 
        !           178: static u32 ssb_pci_setup(struct b44_private *bp, u32 cores)
        !           179: {
        !           180:        u32 bar_orig, pci_rev, val;
        !           181: 
        !           182:        pci_read_config_dword(bp->pci, SSB_BAR0_WIN, &bar_orig);
        !           183:        pci_write_config_dword(bp->pci, SSB_BAR0_WIN,
        !           184:                               BCM4400_PCI_CORE_ADDR);
        !           185:        pci_rev = ssb_get_core_rev(bp);
        !           186: 
        !           187:        val = br32(bp, B44_SBINTVEC);
        !           188:        val |= cores;
        !           189:        bw32(bp, B44_SBINTVEC, val);
        !           190: 
        !           191:        val = br32(bp, SSB_PCI_TRANS_2);
        !           192:        val |= SSB_PCI_PREF | SSB_PCI_BURST;
        !           193:        bw32(bp, SSB_PCI_TRANS_2, val);
        !           194: 
        !           195:        pci_write_config_dword(bp->pci, SSB_BAR0_WIN, bar_orig);
        !           196: 
        !           197:        return pci_rev;
        !           198: }
        !           199: 
        !           200: 
        !           201: static void ssb_core_disable(struct b44_private *bp)
        !           202: {
        !           203:        if (br32(bp, B44_SBTMSLOW) & SBTMSLOW_RESET)
        !           204:                return;
        !           205: 
        !           206:        bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_CLOCK));
        !           207:        b44_wait_bit(bp, B44_SBTMSLOW, SBTMSLOW_REJECT, 100000, 0);
        !           208:        b44_wait_bit(bp, B44_SBTMSHIGH, SBTMSHIGH_BUSY, 100000, 1);
        !           209: 
        !           210:        bw32(bp, B44_SBTMSLOW, (SBTMSLOW_FGC | SBTMSLOW_CLOCK |
        !           211:                                                SSB_CORE_DOWN));
        !           212:        bflush(bp, B44_SBTMSLOW, 1);
        !           213: 
        !           214:        bw32(bp, B44_SBTMSLOW, SSB_CORE_DOWN);
        !           215:        bflush(bp, B44_SBTMSLOW, 1);
        !           216: }
        !           217: 
        !           218: 
        !           219: static void ssb_core_reset(struct b44_private *bp)
        !           220: {
        !           221:        u32 val;
        !           222:        const u32 mask = (SBTMSLOW_CLOCK | SBTMSLOW_FGC | SBTMSLOW_RESET);
        !           223: 
        !           224:        ssb_core_disable(bp);
        !           225: 
        !           226:        bw32(bp, B44_SBTMSLOW, mask);
        !           227:        bflush(bp, B44_SBTMSLOW, 1);
        !           228: 
        !           229:        /* Clear SERR if set, this is a hw bug workaround.  */
        !           230:        if (br32(bp, B44_SBTMSHIGH) & SBTMSHIGH_SERR)
        !           231:                bw32(bp, B44_SBTMSHIGH, 0);
        !           232: 
        !           233:        val = br32(bp, B44_SBIMSTATE);
        !           234:        if (val & (SBIMSTATE_BAD)) {
        !           235:                bw32(bp, B44_SBIMSTATE, val & ~SBIMSTATE_BAD);
        !           236:        }
        !           237: 
        !           238:        bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK | SBTMSLOW_FGC));
        !           239:        bflush(bp, B44_SBTMSLOW, 1);
        !           240: 
        !           241:        bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK));
        !           242:        bflush(bp, B44_SBTMSLOW, 1);
        !           243: }
        !           244: 
        !           245: 
        !           246: /*
        !           247:  * Driver helper functions
        !           248:  */
        !           249: 
        !           250: /*
        !           251:  * Chip reset provides power to the b44 MAC & PCI cores, which
        !           252:  * is necessary for MAC register access. We only do a partial
        !           253:  * reset in case of transmit/receive errors (ISTAT_ERRORS) to
        !           254:  * avoid the chip being hung for an unnecessary long time in
        !           255:  * this case.
        !           256:  *
        !           257:  * Called-by: b44_close, b44_halt, b44_inithw(b44_open), b44_probe
        !           258:  */
        !           259: static void b44_chip_reset(struct b44_private *bp, int reset_kind)
        !           260: {
        !           261:        if (ssb_is_core_up(bp)) {
        !           262:                bw32(bp, B44_RCV_LAZY, 0);
        !           263: 
        !           264:                bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE);
        !           265: 
        !           266:                b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1);
        !           267: 
        !           268:                bw32(bp, B44_DMATX_CTRL, 0);
        !           269: 
        !           270:                bp->tx_dirty = bp->tx_cur = 0;
        !           271: 
        !           272:                if (br32(bp, B44_DMARX_STAT) & DMARX_STAT_EMASK)
        !           273:                        b44_wait_bit(bp, B44_DMARX_STAT, DMARX_STAT_SIDLE,
        !           274:                                                                  100, 0);
        !           275: 
        !           276:                bw32(bp, B44_DMARX_CTRL, 0);
        !           277: 
        !           278:                bp->rx_cur = 0;
        !           279:        } else {
        !           280:                ssb_pci_setup(bp, SBINTVEC_ENET0);
        !           281:        }
        !           282: 
        !           283:        ssb_core_reset(bp);
        !           284: 
        !           285:        /* Don't enable PHY if we are only doing a partial reset. */
        !           286:        if (reset_kind == B44_CHIP_RESET_PARTIAL)
        !           287:                return;
        !           288: 
        !           289:        /* Make PHY accessible. */
        !           290:        bw32(bp, B44_MDIO_CTRL,
        !           291:             (MDIO_CTRL_PREAMBLE | (0x0d & MDIO_CTRL_MAXF_MASK)));
        !           292:        bflush(bp, B44_MDIO_CTRL, 1);
        !           293: 
        !           294:        /* Enable internal or external PHY */
        !           295:        if (!(br32(bp, B44_DEVCTRL) & DEVCTRL_IPP)) {
        !           296:                bw32(bp, B44_ENET_CTRL, ENET_CTRL_EPSEL);
        !           297:                bflush(bp, B44_ENET_CTRL, 1);
        !           298:        } else {
        !           299:                u32 val = br32(bp, B44_DEVCTRL);
        !           300:                if (val & DEVCTRL_EPR) {
        !           301:                        bw32(bp, B44_DEVCTRL, (val & ~DEVCTRL_EPR));
        !           302:                        bflush(bp, B44_DEVCTRL, 100);
        !           303:                }
        !           304:        }
        !           305: }
        !           306: 
        !           307: 
        !           308: /**
        !           309:  * called by b44_poll in the error path
        !           310:  */
        !           311: static void b44_halt(struct b44_private *bp)
        !           312: {
        !           313:        /* disable ints */
        !           314:        bw32(bp, B44_IMASK, 0);
        !           315:        bflush(bp, B44_IMASK, 1);
        !           316: 
        !           317:        DBG("b44: powering down PHY\n");
        !           318:        bw32(bp, B44_MAC_CTRL, MAC_CTRL_PHY_PDOWN);
        !           319: 
        !           320:        /*
        !           321:         * Now reset the chip, but without enabling
        !           322:         * the MAC&PHY part of it.
        !           323:         * This has to be done _after_ we shut down the PHY
        !           324:         */
        !           325:        b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
        !           326: }
        !           327: 
        !           328: 
        !           329: 
        !           330: /*
        !           331:  * Called at device open time to get the chip ready for
        !           332:  * packet processing.
        !           333:  *
        !           334:  * Called-by: b44_open
        !           335:  */
        !           336: static void b44_init_hw(struct b44_private *bp, int reset_kind)
        !           337: {
        !           338:        u32 val;
        !           339: #define CTRL_MASK (DMARX_CTRL_ENABLE | (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT))
        !           340: 
        !           341:        b44_chip_reset(bp, B44_CHIP_RESET_FULL);
        !           342:        if (reset_kind == B44_FULL_RESET) {
        !           343:                b44_phy_reset(bp);
        !           344:        }
        !           345: 
        !           346:        /* Enable CRC32, set proper LED modes and power on PHY */
        !           347:        bw32(bp, B44_MAC_CTRL, MAC_CTRL_CRC32_ENAB | MAC_CTRL_PHY_LEDCTRL);
        !           348:        bw32(bp, B44_RCV_LAZY, (1 << RCV_LAZY_FC_SHIFT));
        !           349: 
        !           350:        /* This sets the MAC address too.  */
        !           351:        b44_set_rx_mode(bp->netdev);
        !           352: 
        !           353:        /* MTU + eth header + possible VLAN tag + struct rx_header */
        !           354:        bw32(bp, B44_RXMAXLEN, B44_MAX_MTU + ETH_HLEN + 8 + RX_HEADER_LEN);
        !           355:        bw32(bp, B44_TXMAXLEN, B44_MAX_MTU + ETH_HLEN + 8 + RX_HEADER_LEN);
        !           356: 
        !           357:        bw32(bp, B44_TX_HIWMARK, TX_HIWMARK_DEFLT);
        !           358:        if (reset_kind == B44_PARTIAL_RESET) {
        !           359:                bw32(bp, B44_DMARX_CTRL, CTRL_MASK);
        !           360:        } else {
        !           361:                bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE);
        !           362:                bw32(bp, B44_DMATX_ADDR, VIRT_TO_B44(bp->tx));
        !           363: 
        !           364:                bw32(bp, B44_DMARX_CTRL, CTRL_MASK);
        !           365:                bw32(bp, B44_DMARX_ADDR, VIRT_TO_B44(bp->rx));
        !           366:                bw32(bp, B44_DMARX_PTR, B44_RX_RING_LEN_BYTES);
        !           367: 
        !           368:                bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ);
        !           369:        }
        !           370: 
        !           371:        val = br32(bp, B44_ENET_CTRL);
        !           372:        bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE));
        !           373: #undef CTRL_MASK
        !           374: }
        !           375: 
        !           376: 
        !           377: /***  Management of ring descriptors  ***/
        !           378: 
        !           379: 
        !           380: static void b44_populate_rx_descriptor(struct b44_private *bp, u32 idx)
        !           381: {
        !           382:        struct rx_header *rh;
        !           383:        u32 ctrl, addr;
        !           384: 
        !           385:        rh = bp->rx_iobuf[idx]->data;
        !           386:        rh->len = 0;
        !           387:        rh->flags = 0;
        !           388:        ctrl = DESC_CTRL_LEN & (RX_PKT_BUF_SZ - RX_PKT_OFFSET);
        !           389:        if (idx == B44_RING_LAST) {
        !           390:                ctrl |= DESC_CTRL_EOT;
        !           391:        }
        !           392:        addr = VIRT_TO_B44(bp->rx_iobuf[idx]->data);
        !           393: 
        !           394:        bp->rx[idx].ctrl = cpu_to_le32(ctrl);
        !           395:        bp->rx[idx].addr = cpu_to_le32(addr);
        !           396:        bw32(bp, B44_DMARX_PTR, idx * sizeof(struct dma_desc));
        !           397: }
        !           398: 
        !           399: 
        !           400: /*
        !           401:  * Refill RX ring descriptors with buffers. This is needed
        !           402:  * because during rx we are passing ownership of descriptor
        !           403:  * buffers to the network stack.
        !           404:  */
        !           405: static void b44_rx_refill(struct b44_private *bp, u32 pending)
        !           406: {
        !           407:        u32 i;
        !           408: 
        !           409:        // skip pending
        !           410:        for (i = pending + 1; i != bp->rx_cur; i = ring_next(i)) {
        !           411:                if (bp->rx_iobuf[i] != NULL)
        !           412:                        continue;
        !           413: 
        !           414:                bp->rx_iobuf[i] = alloc_iob(RX_PKT_BUF_SZ);
        !           415:                if (!bp->rx_iobuf[i]) {
        !           416:                        DBG("Refill rx ring failed!!\n");
        !           417:                        break;
        !           418:                }
        !           419: 
        !           420:                b44_populate_rx_descriptor(bp, i);
        !           421:        }
        !           422: }
        !           423: 
        !           424: 
        !           425: static void b44_free_rx_ring(struct b44_private *bp)
        !           426: {
        !           427:        u32 i;
        !           428: 
        !           429:        if (bp->rx) {
        !           430:                for (i = 0; i < B44_RING_SIZE; i++) {
        !           431:                        free_iob(bp->rx_iobuf[i]);
        !           432:                        bp->rx_iobuf[i] = NULL;
        !           433:                }
        !           434:                free_dma(bp->rx, B44_RX_RING_LEN_BYTES);
        !           435:                bp->rx = NULL;
        !           436:        }
        !           437: }
        !           438: 
        !           439: 
        !           440: static int b44_init_rx_ring(struct b44_private *bp)
        !           441: {
        !           442:        b44_free_rx_ring(bp);
        !           443: 
        !           444:        bp->rx = malloc_dma(B44_RX_RING_LEN_BYTES, B44_DMA_ALIGNMENT);
        !           445:        if (!bp->rx)
        !           446:                return -ENOMEM;
        !           447: 
        !           448:        memset(bp->rx_iobuf, 0, sizeof(bp->rx_iobuf));
        !           449: 
        !           450:        bp->rx_iobuf[0] = alloc_iob(RX_PKT_BUF_SZ);
        !           451:        b44_populate_rx_descriptor(bp, 0);
        !           452:        b44_rx_refill(bp, 0);
        !           453: 
        !           454:        DBG("Init RX rings: rx=0x%08lx\n", VIRT_TO_B44(bp->rx));
        !           455:        return 0;
        !           456: }
        !           457: 
        !           458: 
        !           459: static void b44_free_tx_ring(struct b44_private *bp)
        !           460: {
        !           461:        if (bp->tx) {
        !           462:                free_dma(bp->tx, B44_TX_RING_LEN_BYTES);
        !           463:                bp->tx = NULL;
        !           464:        }
        !           465: }
        !           466: 
        !           467: 
        !           468: static int b44_init_tx_ring(struct b44_private *bp)
        !           469: {
        !           470:        b44_free_tx_ring(bp);
        !           471: 
        !           472:        bp->tx = malloc_dma(B44_TX_RING_LEN_BYTES, B44_DMA_ALIGNMENT);
        !           473:        if (!bp->tx)
        !           474:                return -ENOMEM;
        !           475: 
        !           476:        memset(bp->tx, 0, B44_TX_RING_LEN_BYTES);
        !           477:        memset(bp->tx_iobuf, 0, sizeof(bp->tx_iobuf));
        !           478: 
        !           479:        DBG("Init TX rings: tx=0x%08lx\n", VIRT_TO_B44(bp->tx));
        !           480:        return 0;
        !           481: }
        !           482: 
        !           483: 
        !           484: /*** Interaction with the PHY ***/
        !           485: 
        !           486: 
        !           487: static int b44_phy_read(struct b44_private *bp, int reg, u32 * val)
        !           488: {
        !           489:        int err;
        !           490: 
        !           491:        u32 arg1 = (MDIO_OP_READ << MDIO_DATA_OP_SHIFT);
        !           492:        u32 arg2 = (bp->phy_addr << MDIO_DATA_PMD_SHIFT);
        !           493:        u32 arg3 = (reg << MDIO_DATA_RA_SHIFT);
        !           494:        u32 arg4 = (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT);
        !           495:        u32 argv = arg1 | arg2 | arg3 | arg4;
        !           496: 
        !           497:        bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);
        !           498:        bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | argv));
        !           499:        err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);
        !           500:        *val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA;
        !           501: 
        !           502:        return err;
        !           503: }
        !           504: 
        !           505: 
        !           506: static int b44_phy_write(struct b44_private *bp, int reg, u32 val)
        !           507: {
        !           508:        u32 arg1 = (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT);
        !           509:        u32 arg2 = (bp->phy_addr << MDIO_DATA_PMD_SHIFT);
        !           510:        u32 arg3 = (reg << MDIO_DATA_RA_SHIFT);
        !           511:        u32 arg4 = (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT);
        !           512:        u32 arg5 = (val & MDIO_DATA_DATA);
        !           513:        u32 argv = arg1 | arg2 | arg3 | arg4 | arg5;
        !           514: 
        !           515: 
        !           516:        bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);
        !           517:        bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | argv));
        !           518:        return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);
        !           519: }
        !           520: 
        !           521: 
        !           522: static int b44_phy_reset(struct b44_private *bp)
        !           523: {
        !           524:        u32 val;
        !           525:        int err;
        !           526: 
        !           527:        err = b44_phy_write(bp, MII_BMCR, BMCR_RESET);
        !           528:        if (err)
        !           529:                return err;
        !           530: 
        !           531:        udelay(100);
        !           532:        err = b44_phy_read(bp, MII_BMCR, &val);
        !           533:        if (!err) {
        !           534:                if (val & BMCR_RESET) {
        !           535:                        return -ENODEV;
        !           536:                }
        !           537:        }
        !           538: 
        !           539:        return 0;
        !           540: }
        !           541: 
        !           542: 
        !           543: /*
        !           544:  * The BCM44xx CAM (Content Addressable Memory) stores the MAC
        !           545:  * and PHY address.
        !           546:  */
        !           547: static void b44_cam_write(struct b44_private *bp, unsigned char *data,
        !           548:                                                            int index)
        !           549: {
        !           550:        u32 val;
        !           551: 
        !           552:        val  = ((u32) data[2]) << 24;
        !           553:        val |= ((u32) data[3]) << 16;
        !           554:        val |= ((u32) data[4]) << 8;
        !           555:        val |= ((u32) data[5]) << 0;
        !           556:        bw32(bp, B44_CAM_DATA_LO, val);
        !           557: 
        !           558: 
        !           559:        val = (CAM_DATA_HI_VALID |
        !           560:               (((u32) data[0]) << 8) | (((u32) data[1]) << 0));
        !           561: 
        !           562:        bw32(bp, B44_CAM_DATA_HI, val);
        !           563: 
        !           564:        val = CAM_CTRL_WRITE | (index << CAM_CTRL_INDEX_SHIFT);
        !           565:        bw32(bp, B44_CAM_CTRL, val);
        !           566: 
        !           567:        b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1);
        !           568: }
        !           569: 
        !           570: 
        !           571: static void b44_set_mac_addr(struct b44_private *bp)
        !           572: {
        !           573:        u32 val;
        !           574:        bw32(bp, B44_CAM_CTRL, 0);
        !           575:        b44_cam_write(bp, bp->netdev->ll_addr, 0);
        !           576:        val = br32(bp, B44_CAM_CTRL);
        !           577:        bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
        !           578: }
        !           579: 
        !           580: 
        !           581: /* Read 128-bytes of EEPROM. */
        !           582: static void b44_read_eeprom(struct b44_private *bp, u8 * data)
        !           583: {
        !           584:        long i;
        !           585:        u16 *ptr = (u16 *) data;
        !           586: 
        !           587:        for (i = 0; i < 128; i += 2)
        !           588:                ptr[i / 2] = cpu_to_le16(readw(bp->regs + 4096 + i));
        !           589: }
        !           590: 
        !           591: 
        !           592: static void b44_load_mac_and_phy_addr(struct b44_private *bp)
        !           593: {
        !           594:        u8 eeprom[128];
        !           595: 
        !           596:        /* Load MAC address, note byteswapping */
        !           597:        b44_read_eeprom(bp, &eeprom[0]);
        !           598:        bp->netdev->hw_addr[0] = eeprom[79];
        !           599:        bp->netdev->hw_addr[1] = eeprom[78];
        !           600:        bp->netdev->hw_addr[2] = eeprom[81];
        !           601:        bp->netdev->hw_addr[3] = eeprom[80];
        !           602:        bp->netdev->hw_addr[4] = eeprom[83];
        !           603:        bp->netdev->hw_addr[5] = eeprom[82];
        !           604: 
        !           605:        /* Load PHY address */
        !           606:        bp->phy_addr = eeprom[90] & 0x1f;
        !           607: }
        !           608: 
        !           609: 
        !           610: static void b44_set_rx_mode(struct net_device *netdev)
        !           611: {
        !           612:        struct b44_private *bp = netdev_priv(netdev);
        !           613:        unsigned char zero[6] = { 0, 0, 0, 0, 0, 0 };
        !           614:        u32 val;
        !           615:        int i;
        !           616: 
        !           617:        val = br32(bp, B44_RXCONFIG);
        !           618:        val &= ~RXCONFIG_PROMISC;
        !           619:        val |= RXCONFIG_ALLMULTI;
        !           620: 
        !           621:        b44_set_mac_addr(bp);
        !           622: 
        !           623:        for (i = 1; i < 64; i++)
        !           624:                b44_cam_write(bp, zero, i);
        !           625: 
        !           626:        bw32(bp, B44_RXCONFIG, val);
        !           627:        val = br32(bp, B44_CAM_CTRL);
        !           628:        bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
        !           629: }
        !           630: 
        !           631: 
        !           632: /*** Implementation of iPXE driver callbacks ***/
        !           633: 
        !           634: /**
        !           635:  * Probe device
        !           636:  *
        !           637:  * @v pci      PCI device
        !           638:  * @v id       Matching entry in ID table
        !           639:  * @ret rc     Return status code
        !           640:  */
        !           641: static int b44_probe(struct pci_device *pci)
        !           642: {
        !           643:        struct net_device *netdev;
        !           644:        struct b44_private *bp;
        !           645:        int rc;
        !           646: 
        !           647:        /*
        !           648:         * Bail out if more than 1GB of physical RAM is installed.
        !           649:         * This limitation will be removed later when dma mapping
        !           650:         * is merged into mainline.
        !           651:         */
        !           652:        if (!phys_ram_within_limit(B44_30BIT_DMA_MASK)) {
        !           653:                DBG("Sorry, this version of the driver does not\n"
        !           654:                    "support systems with more than 1GB of RAM.\n");
        !           655:                return -ENOMEM;
        !           656:        }
        !           657: 
        !           658:        /* Set up netdev */
        !           659:        netdev = alloc_etherdev(sizeof(*bp));
        !           660:        if (!netdev)
        !           661:                return -ENOMEM;
        !           662: 
        !           663:        netdev_init(netdev, &b44_operations);
        !           664:        pci_set_drvdata(pci, netdev);
        !           665:        netdev->dev = &pci->dev;
        !           666: 
        !           667:        /* Set up private data */
        !           668:        bp = netdev_priv(netdev);
        !           669:        memset(bp, 0, sizeof(*bp));
        !           670:        bp->netdev = netdev;
        !           671:        bp->pci = pci;
        !           672: 
        !           673:        /* Map device registers */
        !           674:        bp->regs = ioremap(pci->membase, B44_REGS_SIZE);
        !           675:        if (!bp->regs) {
        !           676:                netdev_put(netdev);
        !           677:                return -ENOMEM;
        !           678:        }
        !           679: 
        !           680:        /* Enable PCI bus mastering */
        !           681:        adjust_pci_device(pci);
        !           682: 
        !           683:        b44_load_mac_and_phy_addr(bp);
        !           684: 
        !           685:        rc = register_netdev(netdev);
        !           686:        if (rc != 0) {
        !           687:                iounmap(bp->regs);
        !           688:                netdev_put(netdev);
        !           689:                return rc;
        !           690:        }
        !           691: 
        !           692:        /* Link management currently not implemented */
        !           693:        netdev_link_up(netdev);
        !           694: 
        !           695:        b44_chip_reset(bp, B44_CHIP_RESET_FULL);
        !           696: 
        !           697:        DBG("b44 %s (%04x:%04x) regs=%p MAC=%s\n", pci->id->name,
        !           698:            pci->id->vendor, pci->id->device, bp->regs,
        !           699:            eth_ntoa(netdev->ll_addr));
        !           700: 
        !           701:        return 0;
        !           702: }
        !           703: 
        !           704: 
        !           705: /**
        !           706:  * Remove device
        !           707:  *
        !           708:  * @v pci      PCI device
        !           709:  */
        !           710: static void b44_remove(struct pci_device *pci)
        !           711: {
        !           712:        struct net_device *netdev = pci_get_drvdata(pci);
        !           713:        struct b44_private *bp = netdev_priv(netdev);
        !           714: 
        !           715:        ssb_core_disable(bp);
        !           716:        unregister_netdev(netdev);
        !           717:        iounmap(bp->regs);
        !           718:        netdev_nullify(netdev);
        !           719:        netdev_put(netdev);
        !           720: }
        !           721: 
        !           722: 
        !           723: /** Enable or disable interrupts
        !           724:  *
        !           725:  * @v netdev   Network device
        !           726:  * @v enable   Interrupts should be enabled
        !           727:  */
        !           728: static void b44_irq(struct net_device *netdev, int enable)
        !           729: {
        !           730:        struct b44_private *bp = netdev_priv(netdev);
        !           731: 
        !           732:        /* Interrupt mask specifies which events generate interrupts */
        !           733:        bw32(bp, B44_IMASK, enable ? IMASK_DEF : IMASK_DISABLE);
        !           734: }
        !           735: 
        !           736: 
        !           737: /** Open network device
        !           738:  *
        !           739:  * @v netdev   Network device
        !           740:  * @ret rc     Return status code
        !           741:  */
        !           742: static int b44_open(struct net_device *netdev)
        !           743: {
        !           744:        struct b44_private *bp = netdev_priv(netdev);
        !           745:        int rc;
        !           746: 
        !           747:        rc = b44_init_tx_ring(bp);
        !           748:        if (rc != 0)
        !           749:                return rc;
        !           750: 
        !           751:        rc = b44_init_rx_ring(bp);
        !           752:        if (rc != 0)
        !           753:                return rc;
        !           754: 
        !           755:        b44_init_hw(bp, B44_FULL_RESET);
        !           756: 
        !           757:        /* Disable interrupts */
        !           758:        b44_irq(netdev, 0);
        !           759: 
        !           760:        return 0;
        !           761: }
        !           762: 
        !           763: 
        !           764: /** Close network device
        !           765:  *
        !           766:  * @v netdev   Network device
        !           767:  */
        !           768: static void b44_close(struct net_device *netdev)
        !           769: {
        !           770:        struct b44_private *bp = netdev_priv(netdev);
        !           771: 
        !           772:        b44_chip_reset(bp, B44_FULL_RESET);
        !           773:        b44_free_tx_ring(bp);
        !           774:        b44_free_rx_ring(bp);
        !           775: }
        !           776: 
        !           777: 
        !           778: /** Transmit packet
        !           779:  *
        !           780:  * @v netdev   Network device
        !           781:  * @v iobuf    I/O buffer
        !           782:  * @ret rc     Return status code
        !           783:  */
        !           784: static int b44_transmit(struct net_device *netdev, struct io_buffer *iobuf)
        !           785: {
        !           786:        struct b44_private *bp = netdev_priv(netdev);
        !           787:        u32 cur = bp->tx_cur;
        !           788:        u32 ctrl;
        !           789: 
        !           790:        /* Check for TX ring overflow */
        !           791:        if (bp->tx[cur].ctrl) {
        !           792:                DBG("tx overflow\n");
        !           793:                return -ENOBUFS;
        !           794:        }
        !           795: 
        !           796:        /* Will call netdev_tx_complete() on the iobuf later */
        !           797:        bp->tx_iobuf[cur] = iobuf;
        !           798: 
        !           799:        /* Set up TX descriptor */
        !           800:        ctrl = (iob_len(iobuf) & DESC_CTRL_LEN) |
        !           801:            DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF;
        !           802: 
        !           803:        if (cur == B44_RING_LAST)
        !           804:                ctrl |= DESC_CTRL_EOT;
        !           805: 
        !           806:        bp->tx[cur].ctrl = cpu_to_le32(ctrl);
        !           807:        bp->tx[cur].addr = cpu_to_le32(VIRT_TO_B44(iobuf->data));
        !           808: 
        !           809:        /* Update next available descriptor index */
        !           810:        cur = ring_next(cur);
        !           811:        bp->tx_cur = cur;
        !           812:        wmb();
        !           813: 
        !           814:        /* Tell card that a new TX descriptor is ready */
        !           815:        bw32(bp, B44_DMATX_PTR, cur * sizeof(struct dma_desc));
        !           816:        return 0;
        !           817: }
        !           818: 
        !           819: 
        !           820: /** Recycles sent TX descriptors and notifies network stack
        !           821:  *
        !           822:  * @v bp Driver state
        !           823:  */
        !           824: static void b44_tx_complete(struct b44_private *bp)
        !           825: {
        !           826:        u32 cur, i;
        !           827: 
        !           828:        cur = pending_tx_index(bp);
        !           829: 
        !           830:        for (i = bp->tx_dirty; i != cur; i = ring_next(i)) {
        !           831:                /* Free finished frame */
        !           832:                netdev_tx_complete(bp->netdev, bp->tx_iobuf[i]);
        !           833:                bp->tx_iobuf[i] = NULL;
        !           834: 
        !           835:                /* Clear TX descriptor */
        !           836:                bp->tx[i].ctrl = 0;
        !           837:                bp->tx[i].addr = 0;
        !           838:        }
        !           839:        bp->tx_dirty = cur;
        !           840: }
        !           841: 
        !           842: 
        !           843: static void b44_process_rx_packets(struct b44_private *bp)
        !           844: {
        !           845:        struct io_buffer *iob;  /* received data */
        !           846:        struct rx_header *rh;
        !           847:        u32 pending, i;
        !           848:        u16 len;
        !           849: 
        !           850:        pending = pending_rx_index(bp);
        !           851: 
        !           852:        for (i = bp->rx_cur; i != pending; i = ring_next(i)) {
        !           853:                iob = bp->rx_iobuf[i];
        !           854:                if (iob == NULL)
        !           855:                        break;
        !           856: 
        !           857:                rh = iob->data;
        !           858:                len = le16_to_cpu(rh->len);
        !           859: 
        !           860:                /*
        !           861:                 * Guard against incompletely written RX descriptors.
        !           862:                 * Without this, things can get really slow!
        !           863:                 */
        !           864:                if (len == 0)
        !           865:                        break;
        !           866: 
        !           867:                /* Discard CRC that is generated by the card */
        !           868:                len -= 4;
        !           869: 
        !           870:                /* Check for invalid packets and errors */
        !           871:                if (len > RX_PKT_BUF_SZ - RX_PKT_OFFSET ||
        !           872:                    (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) {
        !           873:                        DBG("rx error len=%d flags=%04x\n", len,
        !           874:                                         cpu_to_le16(rh->flags));
        !           875:                        rh->len = 0;
        !           876:                        rh->flags = 0;
        !           877:                        netdev_rx_err(bp->netdev, iob, -EINVAL);
        !           878:                        continue;
        !           879:                }
        !           880: 
        !           881:                /* Clear RX descriptor */
        !           882:                rh->len = 0;
        !           883:                rh->flags = 0;
        !           884:                bp->rx_iobuf[i] = NULL;
        !           885: 
        !           886:                /* Hand off the IO buffer to the network stack */
        !           887:                iob_reserve(iob, RX_PKT_OFFSET);
        !           888:                iob_put(iob, len);
        !           889:                netdev_rx(bp->netdev, iob);
        !           890:        }
        !           891:        bp->rx_cur = i;
        !           892:        b44_rx_refill(bp, pending_rx_index(bp));
        !           893: }
        !           894: 
        !           895: 
        !           896: /** Poll for completed and received packets
        !           897:  *
        !           898:  * @v netdev   Network device
        !           899:  */
        !           900: static void b44_poll(struct net_device *netdev)
        !           901: {
        !           902:        struct b44_private *bp = netdev_priv(netdev);
        !           903:        u32 istat;
        !           904: 
        !           905:        /* Interrupt status */
        !           906:        istat = br32(bp, B44_ISTAT);
        !           907:        istat &= IMASK_DEF;     /* only the events we care about */
        !           908: 
        !           909:        if (!istat)
        !           910:                return;
        !           911:        if (istat & ISTAT_TX)
        !           912:                b44_tx_complete(bp);
        !           913:        if (istat & ISTAT_RX)
        !           914:                b44_process_rx_packets(bp);
        !           915:        if (istat & ISTAT_ERRORS) {
        !           916:                DBG("b44 error istat=0x%08x\n", istat);
        !           917: 
        !           918:                /* Reset B44 core partially to avoid long waits */
        !           919:                b44_irq(bp->netdev, 0);
        !           920:                b44_halt(bp);
        !           921:                b44_init_tx_ring(bp);
        !           922:                b44_init_rx_ring(bp);
        !           923:                b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY);
        !           924:        }
        !           925: 
        !           926:        /* Acknowledge interrupt */
        !           927:        bw32(bp, B44_ISTAT, 0);
        !           928:        bflush(bp, B44_ISTAT, 1);
        !           929: }
        !           930: 
        !           931: 
        !           932: static struct net_device_operations b44_operations = {
        !           933:        .open = b44_open,
        !           934:        .close = b44_close,
        !           935:        .transmit = b44_transmit,
        !           936:        .poll = b44_poll,
        !           937:        .irq = b44_irq,
        !           938: };
        !           939: 
        !           940: 
        !           941: static struct pci_device_id b44_nics[] = {
        !           942:        PCI_ROM(0x14e4, 0x4401, "BCM4401", "BCM4401", 0),
        !           943:        PCI_ROM(0x14e4, 0x170c, "BCM4401-B0", "BCM4401-B0", 0),
        !           944:        PCI_ROM(0x14e4, 0x4402, "BCM4401-B1", "BCM4401-B1", 0),
        !           945: };
        !           946: 
        !           947: 
        !           948: struct pci_driver b44_driver __pci_driver = {
        !           949:        .ids = b44_nics,
        !           950:        .id_count = sizeof b44_nics / sizeof b44_nics[0],
        !           951:        .probe = b44_probe,
        !           952:        .remove = b44_remove,
        !           953: };

unix.superglobalmegacorp.com

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