分类: 嵌入式
2013-06-27 11:36:41
错误现象:
系统启动后,ping网口,插拔网线后,就不能ping通,必须ifconfig重新启动网口才能ping通
原因分析:
插上网线后,产生phy中断, 从中断到检查link stat自动协商比较慢,但是我们的代码中没有延时,所以导致检查失败;
解决方法:
产生PHY中断后,延时一段时间,再检查link stat
dm9000 采用工作队列的方式,delay一段时间后再查看寄存器。
dm9000_probe(struct platform_dev *pdev)
{
…
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
…
}
static irqreturn_t dm9000_interrupt(int irq, void *devid)
{
…
if (db->type != TYPE_DM9000E) {
if (int_status & ISR_LNKCHNG) {
/* fire a link-change request */
schedule_delayed_work(&db->phy_poll, 1);
}
}
…
}
static void
dm9000_poll_work(struct work_struct *w)
{
struct delayed_work *dw = to_delayed_work(w);
board_info_t *db = container_of(dw, board_info_t, phy_poll);
struct net_device *ndev = db->ndev;
if (db->flags & DM9000_PLATF_SIMPLE_PHY &&
!(db->flags & DM9000_PLATF_EXT_PHY)) {
unsigned nsr = dm9000_read_locked(db, DM9000_NSR);
unsigned old_carrier = netif_carrier_ok(ndev) ? 1 : 0;
unsigned new_carrier;
new_carrier = (nsr & NSR_LINKST) ? 1 : 0;
if (old_carrier != new_carrier) {
if (netif_msg_link(db))
dm9000_show_carrier(db, new_carrier, nsr);
if (!new_carrier)
netif_carrier_off(ndev);
else
netif_carrier_on(ndev);
}
} else
mii_check_media(&db->mii, netif_msg_link(db), 0);
if (netif_running(ndev))
dm9000_schedule_poll(db);
}
static void dm9000_schedule_poll(board_info_t *db)
{
if (db->type == TYPE_DM9000E)
schedule_delayed_work(&db->phy_poll, HZ * 2);
}
参考dm9000的代码,但是这里我们不采用延时的工作队列的办法而采用中断线程的方法,在其他文章已经介绍过request_threaded_irq的使用方法
static irqreturn_t at91ether_phy_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
struct at91_private *lp = netdev_priv(dev);
unsigned int phy;
/*
* This hander is triggered on both edges, but the PHY chips expect
* level-triggering. We therefore have to check if the PHY actually has
* an IRQ pending.
*/
enable_mdi();
if ((lp->phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID) || (lp->phy_type == MII_DM9161C_ID)) {
read_phy(lp->phy_address, MII_DSINTR_REG, &phy); /* ack interrupt in Davicom PHY */
if (!(phy & (1 << 0)))
goto done;
else
printk(KERN_DEBUG "In interrupt function : %s, REG21=%x\n", __FUNCTION__, phy);
}
else if (lp->phy_type == MII_LXT971A_ID) {
read_phy(lp->phy_address, MII_ISINTS_REG, &phy); /* ack interrupt in Intel PHY */
if (!(phy & (1 << 2)))
goto done;
}
else if (lp->phy_type == MII_BCM5221_ID) {
read_phy(lp->phy_address, MII_BCMINTR_REG, &phy); /* ack interrupt in Broadcom PHY */
if (!(phy & (1 << 0)))
goto done;
}
else if ((lp->phy_type == MII_KS8721_ID) || (lp->phy_type == MII_KSZ8041_ID)) {
read_phy(lp->phy_address, MII_TPISTATUS, &phy); /* ack interrupt in Micrel PHY */
if (!(phy & ((1 << 2) | 1)))
goto done;
}
else if (lp->phy_type == MII_T78Q21x3_ID) { /* ack interrupt in Teridian PHY */
read_phy(lp->phy_address, MII_T78Q21INT_REG, &phy);
if (!(phy & ((1 << 2) | 1)))
goto done;
}
else if (lp->phy_type == MII_DP83848_ID) {
read_phy(lp->phy_address, MII_DPPHYSTS_REG, &phy); /* ack interrupt in DP83848 PHY */
if (!(phy & (1 << 7)))
goto done;
}
else if (lp->phy_type == MII_STE100P_ID) { /* ack interrupt in STE100P PHY */
read_phy(lp->phy_address, MII_STE100P_XCSIIS_REG, &phy);
if (!(phy & 0x007F))
goto done;
}
/* update_linkspeed move to irq thread_fn */
// update_linkspeed(dev, 0);
done:
disable_mdi();
return IRQ_WAKE_THREAD;
}
static irqreturn_t at91ether_phy_work(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
struct at91_private *lp = netdev_priv(dev);
unsigned int phy;
msleep(2000);
enable_mdi();
update_linkspeed(dev, 0);
disable_mdi();
return IRQ_HANDLED;
}
申请中断:
…
status = request_threaded_irq(irq_number, at91ether_phy_interrupt, at91ether_phy_work,0, dev->name, dev);
if (status) {
printk(KERN_ERR "at91_ether: PHY IRQ %d request failed - status %d!\n", irq_number, status);
return;
}
…