⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 921x_4_linux.c

📁 ethernet device driver for smsc9115
💻 C
📖 第 1 页 / 共 5 页
字号:
}

/* NAPI poll function */
static int smsc911x_poll(struct net_device *dev, int *budget)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    int npackets = 0;
    int quota = min(dev->quota, *budget);

    while (npackets < quota) {
        unsigned int pktlength;
        unsigned int pktwords;
        unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);

        /* break out of while loop if there are no more packets waiting */
        if (!rxstat)
            break;

        pktlength = ((rxstat & 0x3FFF0000) >> 16);
        pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
        smsc911x_rx_counterrors(pdata, rxstat);

        if (likely((rxstat & RX_STS_ES_) == 0)) {
            struct sk_buff *skb;
            skb = dev_alloc_skb(pktlength + NET_IP_ALIGN);
            if (likely(skb)) {
                skb->data = skb->head;
                skb->tail = skb->head;
                /* Align IP on 16B boundary */
                skb_reserve(skb, NET_IP_ALIGN);
                skb_put(skb, pktlength - 4);
                smsc911x_rx_readfifo(pdata,
                             (unsigned int *)skb->head,
                             pktwords);
                skb->dev = dev;
                skb->protocol = eth_type_trans(skb, dev);
                skb->ip_summed = CHECKSUM_NONE;
                netif_receive_skb(skb);

                /* Update counters */
                pdata->stats.rx_packets++;
                pdata->stats.rx_bytes += (pktlength - 4);
                dev->last_rx = jiffies;
                npackets++;
                continue;
            } else {
                SMSC_WARNING("Unable to allocate sk_buff "
                         "for rx packet, in PIO path");
                pdata->stats.rx_dropped++;
            }
        }
        /* At this point, the packet is to be read out
         * of the fifo and discarded */
        smsc911x_rx_fastforward(pdata, pktwords);
    }

    pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
    smsc911x_reg_write(INT_STS_RSFL_, pdata, INT_STS);

    *budget -= npackets;
    dev->quota -= npackets;

    if (npackets < quota) {
        unsigned int temp;
        /* We processed all packets available.  Tell NAPI it can
         * stop polling then re-enable rx interrupts */
        netif_rx_complete(dev);
        temp = smsc911x_reg_read(pdata, INT_EN);
        temp |= INT_EN_RSFL_EN_;
        smsc911x_reg_write(temp, pdata, INT_EN);
        return 0;
    }

    /* There are still packets waiting */
    return 1;
}

/* Returns hash bit number for given MAC address
 * Example:
 * 01 00 5E 00 00 01 -> returns bit number 31 */
static unsigned int smsc911x_hash(char addr[ETH_ALEN])
{
    unsigned int crc;
    unsigned int result;

    crc = ether_crc(ETH_ALEN, addr);
    result = (crc >> 26) & 0x3f;

    return result;
}

static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
{
    /* Performs the multicast & mac_cr update.  This is called when
     * safe on the current hardware, and with the phy_lock held */
    unsigned int mac_cr = smsc911x_mac_read(pdata, MAC_CR);
    mac_cr |= pdata->set_bits_mask;
    mac_cr &= ~(pdata->clear_bits_mask);
    smsc911x_mac_write(pdata, MAC_CR, mac_cr);
    smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
    smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
    SMSC_TRACE("maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X", mac_cr,
           pdata->hashhi, pdata->hashlo);
}

static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
{
    unsigned int mac_cr;

    /* This function is only called for older LAN911x devices 
     * (revA or revB), where MAC_CR, HASHH and HASHL should not
     * be modified during Rx - newer devices immediately update the
     * registers.
     *
     * This is called from interrupt context */

    spin_lock(&pdata->phy_lock);

    /* Check Rx has stopped */
    if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
        SMSC_WARNING("Rx not stopped\n");

    /* Perform the update - safe to do now Rx has stopped */
    smsc911x_rx_multicast_update(pdata);

    /* Re-enable Rx */
    mac_cr = smsc911x_mac_read(pdata, MAC_CR);
    mac_cr |= MAC_CR_RXEN_;
    smsc911x_mac_write(pdata, MAC_CR, mac_cr);

    pdata->multicast_update_pending = 0;

    spin_unlock(&pdata->phy_lock);
}

static int smsc911x_soft_reset(struct smsc911x_data *pdata)
{
    unsigned int timeout;
    unsigned int temp;

    /* Reset the LAN911x */
    smsc911x_reg_write(HW_CFG_SRST_, pdata, HW_CFG);
    timeout = 10;
    do {
        udelay(10);
        temp = smsc911x_reg_read(pdata, HW_CFG);
    } while ((--timeout) && (temp & HW_CFG_SRST_));

    if (unlikely(temp & HW_CFG_SRST_)) {
        SMSC_WARNING("Failed to complete reset");
        return -ENODEV;
    }
    return 0;
}

static int smsc911x_open(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned int mac_high16;
    unsigned int mac_low32;
    unsigned int timeout;
    unsigned int temp;

    spin_lock_init(&pdata->phy_lock);

    /* Reset the LAN911x */
    if (smsc911x_soft_reset(pdata))
        return -ENODEV;

    smsc911x_reg_write(0x00050000, pdata, HW_CFG);
    smsc911x_reg_write(0x006E3740, pdata, AFC_CFG);

    /* Make sure EEPROM has finished loading before setting GPIO_CFG */
    timeout = 50;
    while ((timeout--) &&
           (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
        udelay(10);
    }

    if (unlikely(timeout == 0)) {
        SMSC_WARNING("Timed out waiting for EEPROM "
                 "busy bit to clear\n");
    }
#if USE_DEBUG >= 1
    smsc911x_reg_write(0x00670700, pdata, GPIO_CFG);
#else
    smsc911x_reg_write(0x70070000, pdata, GPIO_CFG);
#endif

    /* Initialise irqs, but leave all sources disabled */
    smsc911x_reg_write(0, pdata, INT_EN);
    smsc911x_reg_write(0xFFFFFFFF, pdata, INT_STS);
    /* Set interrupt deassertion to 100uS */
    smsc911x_reg_write(((10 << 24) | INT_CFG_IRQ_EN_), pdata, INT_CFG);

    /*
     * intcfg |= INT_CFG_IRQ_POL_;  use this to set IRQ_POL bit
     * intcfg |= INT_CFG_IRQ_TYPE_;  use this to set IRQ_TYPE bit
     */

    SMSC_TRACE("Testing irq handler using IRQ %d", dev->irq);
    pdata->software_irq_signal = 0;
    smp_wmb();

    temp = smsc911x_reg_read(pdata, INT_EN);
    temp |= INT_EN_SW_INT_EN_;
    smsc911x_reg_write(temp, pdata, INT_EN);

    timeout = 1000;
    while (timeout--) {
        smp_rmb();
        if (pdata->software_irq_signal)
            break;
        //msleep(1);
        mdelay(1);
    }

    if (!pdata->software_irq_signal) {
        printk(KERN_WARNING "%s: ISR failed signaling test (IRQ %d)\n",
               dev->name, dev->irq);
        return -ENODEV;
    }
    SMSC_TRACE("IRQ handler passed test using IRQ %d", dev->irq);

    printk(KERN_INFO "%s: SMSC911x/921x identified at %#08lx, IRQ: %d\n",
           dev->name, (unsigned long)pdata->ioaddr, dev->irq);

    spin_lock_irq(&pdata->phy_lock);

    /* Read mac address from EEPROM */
    mac_high16 = smsc911x_mac_read(pdata, ADDRH);
    mac_low32 = smsc911x_mac_read(pdata, ADDRL);

    /* Generate random MAC address if eeprom values are invalid */
    if ((mac_high16 == 0x0000FFFF) && (mac_low32 == 0xFFFFFFFF)) {
        u8 random_mac[6];
        random_ether_addr(random_mac);
        mac_high16 = (random_mac[5] << 8) | random_mac[4];
        mac_low32 = (random_mac[3] << 24) | (random_mac[2] << 16) |
            (random_mac[1] << 8) | random_mac[0];

        smsc911x_mac_write(pdata, ADDRH, mac_high16);
        smsc911x_mac_write(pdata, ADDRL, mac_low32);
        SMSC_TRACE("MAC Address is set to random_ether_addr");
    } else {
        SMSC_TRACE("Mac Address is read from LAN911x EEPROM");
    }

    spin_unlock_irq(&pdata->phy_lock);

    dev->dev_addr[0] = (u8)(mac_low32);
    dev->dev_addr[1] = (u8)(mac_low32 >> 8);
    dev->dev_addr[2] = (u8)(mac_low32 >> 16);
    dev->dev_addr[3] = (u8)(mac_low32 >> 24);
    dev->dev_addr[4] = (u8)(mac_high16);
    dev->dev_addr[5] = (u8)(mac_high16 >> 8);
    printk(KERN_INFO
           "%s: SMSC911x MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
           dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
           dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);

    netif_carrier_off(dev);
    if (!smsc911x_phy_initialise(dev)) {
        SMSC_WARNING("Failed to initialize PHY");
        return -ENODEV;
    }

    temp = smsc911x_reg_read(pdata, HW_CFG);
    /* Preserve TX FIFO size and external PHY configuration */
    temp &= (HW_CFG_TX_FIF_SZ_|0x00000FFF);
    temp |= HW_CFG_SF_;
    smsc911x_reg_write(temp, pdata, HW_CFG);

    temp = smsc911x_reg_read(pdata, FIFO_INT);
    temp |= FIFO_INT_TX_AVAIL_LEVEL_;
    temp &= ~(FIFO_INT_RX_STS_LEVEL_);
    smsc911x_reg_write(temp, pdata, FIFO_INT);

    /* set RX Data offset to 2 bytes for alignment */
    smsc911x_reg_write((2 << 8), pdata, RX_CFG);

    temp = smsc911x_reg_read(pdata, INT_EN);
    temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_PHY_INT_EN_);
    smsc911x_reg_write(temp, pdata, INT_EN);

    spin_lock_irq(&pdata->phy_lock);
    temp = smsc911x_mac_read(pdata, MAC_CR);
    temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
    smsc911x_mac_write(pdata, MAC_CR, temp);
    spin_unlock_irq(&pdata->phy_lock);

    smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);

    netif_start_queue(dev);
    return 0;
}

/* Entry point for stopping the interface */
static int smsc911x_stop(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);

    pdata->stop_link_poll = 1;
    del_timer_sync(&pdata->link_poll_timer);

    smsc911x_reg_write((smsc911x_reg_read(pdata, INT_CFG) &
                (~INT_CFG_IRQ_EN_)), pdata, INT_CFG);
    netif_stop_queue(dev);

    /* At this point all Rx and Tx activity is stopped */
    pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
    smsc911x_tx_update_txcounters(pdata);

    SMSC_TRACE("<--Simp911x_stop");
    return 0;
}

/* Entry point for transmitting a packet */
static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned int freespace;
    unsigned int tx_cmd_a;
    unsigned int tx_cmd_b;
    unsigned int temp;
    u32 wrsz;
    u32 bufp;

    freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;

    if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
        SMSC_WARNING("Tx data fifo low, space available: %d",
                 freespace);

    /* Word alignment adjustment */
    tx_cmd_a = ((((unsigned int)(skb->data)) & 0x03) << 16);
    tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
    tx_cmd_a |= (unsigned int)skb->len;

    tx_cmd_b = ((unsigned int)skb->len) << 16;
    tx_cmd_b |= (unsigned int)skb->len;

    smsc911x_reg_write(tx_cmd_a, pdata, TX_DATA_FIFO);
    smsc911x_reg_write(tx_cmd_b, pdata, TX_DATA_FIFO);

    bufp = ((u32)skb->data) & 0xFFFFFFFC;
    wrsz = (u32)skb->len + 3;
    wrsz += ((u32)skb->data) & 0x3;
    wrsz >>= 2;

    smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
    dev_kfree_skb(skb);
    freespace -= (skb->len + 32);
    dev->trans_start = jiffies;

    if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
        smsc911x_tx_update_txcounters(pdata);

    if (freespace < TX_FIFO_LOW_THRESHOLD) {
        netif_stop_queue(dev);
        temp = smsc911x_reg_read(pdata, FIFO_INT);
        temp &= 0x00FFFFFF;
        temp |= 0x32000000;
        smsc911x_reg_write(temp, pdata, FIFO_INT);
    }

    return NETDEV_TX_OK;
}

/* Entry point for getting status counters */
static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    smsc911x_tx_update_txcounters(pdata);
    return &pdata->stats;
}

/* Entry point for setting addressing modes */
static void smsc911x_set_multicast_list(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned long flags;

    if (dev->flags & IFF_PROMISC) {
        /* Enabling promiscuous mode */
        pdata->set_bits_mask = MAC_CR_PRMS_;
        pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
        pdata->hashhi = 0;
        pdata->hashlo = 0;
    } else if (dev->flags & IFF_ALLMULTI) {
        /* Enabling all multicast mode */
        pdata->set_bits_mask = MAC_CR_MCPAS_;
        pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
        pdata->hashhi = 0;
        pdata->hashlo = 0;
    } else if (dev->mc_count > 0) {
        /* Enabling specific multicast addresses */
        unsigned int hash_high = 0;
        unsigned int hash_low = 0;
        unsigned int count = 0;
        struct dev_mc_list *mc_list = dev->mc_list;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -