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

📄 921x_4_linux.c

📁 ethernet device driver for smsc9115
💻 C
📖 第 1 页 / 共 5 页
字号:
        pdata->set_bits_mask = MAC_CR_HPFILT_;
        pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);

        while (mc_list) {
            count++;
            if ((mc_list->dmi_addrlen) == ETH_ALEN) {
                unsigned int bitnum =
                    smsc911x_hash(mc_list->dmi_addr);
                unsigned int mask = 0x01 << (bitnum & 0x1F);
                if (bitnum & 0x20)
                    hash_high |= mask;
                else
                    hash_low |= mask;
            } else {
                SMSC_WARNING("dmi_addrlen != 6");
            }
            mc_list = mc_list->next;
        }
        if (count != (unsigned int)dev->mc_count)
            SMSC_WARNING("mc_count != dev->mc_count");

        pdata->hashhi = hash_high;
        pdata->hashlo = hash_low;
    } else {
        /* Enabling local MAC address only */
        pdata->set_bits_mask = 0;
        pdata->clear_bits_mask =
            (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
        pdata->hashhi = 0;
        pdata->hashlo = 0;
    }

    spin_lock_irqsave(&pdata->phy_lock, flags);

    if (pdata->generation <= 1) {
        /* Older hardware revision - cannot change these flags while
         * receiving data */
        if (pdata->multicast_update_pending == 0) {
            unsigned int temp;
            SMSC_TRACE("scheduling mcast update");
            pdata->multicast_update_pending = 1;

            /* Request the hardware to stop, then perform the
             * update when we get an RX_STOP interrupt */
            smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
            temp = smsc911x_reg_read(pdata, INT_EN);
            temp |= INT_EN_RXSTOP_INT_EN_;
            smsc911x_reg_write(temp, pdata, INT_EN);

            temp = smsc911x_mac_read(pdata, MAC_CR);
            temp &= ~(MAC_CR_RXEN_);
            smsc911x_mac_write(pdata, MAC_CR, temp);
        } else {
            /* There is another update pending, this should now
             * use the newer values */
        }
    } else {
        /* Newer hardware revision - can write immediately */
        smsc911x_rx_multicast_update(pdata);
    }

    spin_unlock_irqrestore(&pdata->phy_lock, flags);
}

static void smsc911x_irqhandler(int irq, void *dev_id, struct pt_regs *regs)
{
    struct net_device *dev = dev_id;
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned int intsts;
    unsigned int inten;
    unsigned int temp;
    int serviced = 0;

    intsts = smsc911x_reg_read(pdata, INT_STS);
    inten = smsc911x_reg_read(pdata, INT_EN);

    if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
        temp = smsc911x_reg_read(pdata, INT_EN);
        temp &= (~INT_EN_SW_INT_EN_);
        smsc911x_reg_write(temp, pdata, INT_EN);
        smsc911x_reg_write(INT_STS_SW_INT_, pdata, INT_STS);
        pdata->software_irq_signal = 1;
        smp_wmb();
        serviced = 1;
    }

    if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
        /* Called when there is a multicast update scheduled and
         * it is now safe to complete the update */
        SMSC_TRACE("RX Stop interrupt");
        temp = smsc911x_reg_read(pdata, INT_EN);
        temp &= (~INT_EN_RXSTOP_INT_EN_);
        smsc911x_reg_write(temp, pdata, INT_EN);
        smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
        smsc911x_rx_multicast_update_workaround(pdata);
        serviced = 1;
    }

    if (intsts & inten & INT_STS_TDFA_) {
        temp = smsc911x_reg_read(pdata, FIFO_INT);
        temp |= FIFO_INT_TX_AVAIL_LEVEL_;
        smsc911x_reg_write(temp, pdata, FIFO_INT);
        smsc911x_reg_write(INT_STS_TDFA_, pdata, INT_STS);
        netif_wake_queue(dev);
        serviced = 1;
    }

    if (unlikely(intsts & inten & INT_STS_RXE_)) {
        smsc911x_reg_write(INT_STS_RXE_, pdata, INT_STS);
        serviced = 1;
    }

    if (likely(intsts & inten & INT_STS_RSFL_)) {
        /* Disable Rx interrupts and schedule NAPI poll */
        temp = smsc911x_reg_read(pdata, INT_EN);
        temp &= (~INT_EN_RSFL_EN_);
        smsc911x_reg_write(temp, pdata, INT_EN);
        netif_rx_schedule(dev);
        serviced = 1;
    }

    if (unlikely(intsts & inten & INT_STS_PHY_INT_)) {
        smsc911x_reg_write(INT_STS_PHY_INT_, pdata, INT_STS);
        spin_lock(&pdata->phy_lock);
        temp = smsc911x_phy_read(pdata, MII_INTSTS);
        spin_unlock(&pdata->phy_lock);
        SMSC_TRACE("PHY interrupt, sts 0x%04X", (u16)temp);
        smsc911x_phy_update_linkmode(dev, 0);
        serviced = 1;
    }
}

#ifdef CONFIG_NET_POLL_CONTROLLER
void smsc911x_poll_controller(struct net_device *dev)
{
    disable_irq(dev->irq);
    smsc911x_irqhandler(0, dev, NULL);
    enable_irq(dev->irq);
}
#endif                /* CONFIG_NET_POLL_CONTROLLER */

/* Standard ioctls for mii-tool */
static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    struct mii_ioctl_data *data = if_mii(ifr);
    unsigned long flags;

    SMSC_TRACE("ioctl cmd 0x%x", cmd);
    switch (cmd) {
    case SIOCGMIIPHY:
        data->phy_id = pdata->mii.phy_id;
        return 0;
    case SIOCGMIIREG:
        spin_lock_irqsave(&pdata->phy_lock, flags);
        data->val_out = smsc911x_phy_read(pdata, data->reg_num);
        spin_unlock_irqrestore(&pdata->phy_lock, flags);
        return 0;
    case SIOCSMIIREG:
        spin_lock_irqsave(&pdata->phy_lock, flags);
        smsc911x_phy_write(pdata, data->reg_num, data->val_in);
        spin_unlock_irqrestore(&pdata->phy_lock, flags);
        return 0;
    }

    SMSC_TRACE("unsupported ioctl cmd");
    return -1;
}

static int
smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
    struct smsc911x_data *pdata = netdev_priv(dev);

    cmd->maxtxpkt = 1;
    cmd->maxrxpkt = 1;
    return mii_ethtool_gset(&pdata->mii, cmd);
}

static int
smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
    struct smsc911x_data *pdata = netdev_priv(dev);

    return mii_ethtool_sset(&pdata->mii, cmd);
}
/*
static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
                    struct ethtool_drvinfo *info)
{
    strncpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
    strncpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
    strncpy(info->bus_info, dev->class_dev.dev->bus_id,
        sizeof(info->bus_info));
}
*/
static int smsc911x_ethtool_nwayreset(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);

    return mii_nway_restart(&pdata->mii);
}

static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    return pdata->msg_enable;
}

static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    pdata->msg_enable = level;
}

static int smsc911x_ethtool_getregslen(struct net_device *dev)
{
    return (((E2P_DATA - ID_REV) / 4) + 1 + (WUCSR - MAC_CR) + 1 + 32) *
        sizeof(u32);
}

static void
smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
             void *buf)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned long flags;
    unsigned int i;
    unsigned int j = 0;
    u32 *data = buf;

    regs->version = pdata->idrev;
    for (i = ID_REV; i <= E2P_DATA; i += (sizeof(u32)))
        data[j++] = smsc911x_reg_read(pdata, i);

    spin_lock_irqsave(&pdata->phy_lock, flags);
    for (i = MAC_CR; i <= WUCSR; i++)
        data[j++] = smsc911x_mac_read(pdata, i);
    for (i = 0; i <= 31; i++)
        data[j++] = smsc911x_phy_read(pdata, i);
    spin_unlock_irqrestore(&pdata->phy_lock, flags);
}

static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
{
    unsigned int temp;
    temp = smsc911x_reg_read(pdata, GPIO_CFG);
    temp &= ~GPIO_CFG_EEPR_EN_;
    smsc911x_reg_write(temp, pdata, GPIO_CFG);
    //msleep(1);
    mdelay(1);
}

static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
{
    int timeout = 100;
    u32 e2cmd;

    SMSC_TRACE("op 0x%08x", op);
    if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
        SMSC_WARNING("Busy at start");
        return -EBUSY;
    }

    e2cmd = op | E2P_CMD_EPC_BUSY_;
    smsc911x_reg_write(e2cmd, pdata, E2P_CMD);

    do {
        //msleep(1);
        mdelay(1);
        e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
    } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));

    if (!timeout) {
        SMSC_TRACE("TIMED OUT");
        return -EAGAIN;
    }

    if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
        SMSC_TRACE("Error occured during eeprom operation");
        return -EINVAL;
    }

    return 0;
}

static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
                     u8 address, u8 *data)
{
    u32 op = E2P_CMD_EPC_CMD_READ_ | address;
    int ret;

    SMSC_TRACE("address 0x%x", address);
    ret = smsc911x_eeprom_send_cmd(pdata, op);

    if (!ret)
        data[address] = smsc911x_reg_read(pdata, E2P_DATA);

    return ret;
}

static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
                      u8 address, u8 data)
{
    u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
    int ret;

    SMSC_TRACE("address 0x%x, data 0x%x", address, data);
    ret = smsc911x_eeprom_send_cmd(pdata, op);

    if (!ret) {
        op = E2P_CMD_EPC_CMD_WRITE_ | address;
        smsc911x_reg_write((u32)data, pdata, E2P_DATA);
        ret = smsc911x_eeprom_send_cmd(pdata, op);
    }

    return ret;
}

static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
{
    return SMSC911X_EEPROM_SIZE;
}

static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
                       struct ethtool_eeprom *eeprom, u8 *data)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    u8 eeprom_data[SMSC911X_EEPROM_SIZE];
    int len;
    int i;

    smsc911x_eeprom_enable_access(pdata);

    len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
    for (i = 0; i < len; i++) {
        int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
        if (ret < 0) {
            eeprom->len = 0;
            return ret;
        }
    }

    memcpy(data, &eeprom_data[eeprom->offset], len);
    eeprom->len = len;
    return 0;
}

static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
                       struct ethtool_eeprom *eeprom, u8 *data)
{
    int ret;
    struct smsc911x_data *pdata = netdev_priv(dev);

    smsc911x_eeprom_enable_access(pdata);
    smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
    ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
    smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);

    /* Single byte write, according to man page */
    eeprom->len = 1;

    return ret;
}
/*
static struct ethtool_ops smsc911x_ethtool_ops = {
    .get_settings = smsc911x_ethtool_getsettings,
    .set_settings = smsc911x_ethtool_setsettings,
    .get_link = ethtool_op_get_link,
    .get_drvinfo = smsc911x_ethtool_getdrvinfo,
    .nway_reset = smsc911x_ethtool_nwayreset,
    .get_msglevel = smsc911x_ethtool_getmsglevel,
    .set_msglevel = smsc911x_ethtool_setmsglevel,
    .get_regs_len = smsc911x_ethtool_getregslen,
    .get_regs = smsc911x_ethtool_getregs,
    .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
    .get_eeprom = smsc911x_ethtool_get_eeprom,
    .set_eeprom = smsc911x_ethtool_set_eeprom,
};
*/
/* Initializing private device structures */
static int smsc911x_init(struct net_device *dev)
{
    struct smsc911x_data *pdata = netdev_priv(dev);

    SMSC_TRACE("Driver Parameters:");
    SMSC_TRACE("LAN base: 0x%08lX", (unsigned long)pdata->ioaddr);
    SMSC_TRACE("IRQ: %d", dev->irq);
    SMSC_TRACE("PHY will be autodetected.");

    if (pdata->ioaddr == 0) {
        SMSC_WARNING("pdata->ioaddr: 0x00000000");
        return -ENODEV;
    }

    /* Default generation to zero (all workarounds apply) */
    pdata->generation = 0;

    pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
    if (((pdata->idrev >> 16) & 0xFFFF) == (pdata->idrev & 0xFFFF)) {
        SMSC_WARNING("idrev top 16 bits equal to bottom 16 bits, "
                 "idrev: 0x%08X", pdata->idrev);

⌨️ 快捷键说明

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