From 77923034a0af327fc71298d9ecfcf25d9c3a15bc Mon Sep 17 00:00:00 2001 From: lyx Date: Tue, 20 Sep 2011 02:38:52 -0700 Subject: [PATCH] rk29 vmac: improve vmac suspend & resume function MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 1.解决因为以太网卡休眠唤醒概率性死锁,导致系统重启的bug --- drivers/net/rk29_vmac.c | 146 +++++++++++++++++++++------------------- drivers/net/rk29_vmac.h | 4 ++ 2 files changed, 82 insertions(+), 68 deletions(-) diff --git a/drivers/net/rk29_vmac.c b/drivers/net/rk29_vmac.c index 561b7928179f..e8b72fc8ba5e 100755 --- a/drivers/net/rk29_vmac.c +++ b/drivers/net/rk29_vmac.c @@ -173,9 +173,9 @@ static void vmac_handle_link_change(struct net_device *dev) static int __devinit vmac_mii_probe(struct net_device *dev) { struct vmac_priv *ap = netdev_priv(dev); - struct phy_device *phydev = NULL; - struct clk *sys_clk; - unsigned long clock_rate; + struct phy_device *phydev = NULL; + //struct clk *sys_clk; + //unsigned long clock_rate; int phy_addr, err; /* find the first phy */ @@ -234,9 +234,8 @@ static int __devinit vmac_mii_probe(struct net_device *dev) ap->phy_dev = phydev; return 0; - -err_disconnect: - phy_disconnect(phydev); +//err_disconnect: +// phy_disconnect(phydev); err_out: return err; } @@ -287,6 +286,7 @@ err_out_free_mdio_irq: kfree(ap->mii_bus->irq); err_out: mdiobus_free(ap->mii_bus); + ap->mii_bus = NULL; return err; } @@ -296,10 +296,12 @@ static void vmac_mii_exit(struct net_device *dev) if (ap->phy_dev) phy_disconnect(ap->phy_dev); - - mdiobus_unregister(ap->mii_bus); - kfree(ap->mii_bus->irq); - mdiobus_free(ap->mii_bus); + if (ap->mii_bus) { + mdiobus_unregister(ap->mii_bus); + kfree(ap->mii_bus->irq); + mdiobus_free(ap->mii_bus); + ap->mii_bus = NULL; + } } static int vmacether_get_settings(struct net_device *dev, @@ -1035,6 +1037,8 @@ int vmac_open(struct net_device *dev) if (ap == NULL) return -ENODEV; + wake_lock_timeout(&ap->resume_lock, 5*HZ); + ap->shutdown = 0; //set rmii ref clock 50MHz @@ -1113,6 +1117,9 @@ int vmac_open(struct net_device *dev) dev_info(&ap->pdev->dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + ap->suspending = 0; + ap->open_flag = 1; + return 0; err_free_irq: @@ -1136,6 +1143,11 @@ int vmac_close(struct net_device *dev) struct rk29_vmac_platform_data *pdata = ap->pdev->dev.platform_data; printk("enter func %s...\n", __func__); + + if (ap->suspending == 1) + return 0; + + ap->open_flag = 0; netif_stop_queue(dev); napi_disable(&ap->napi); @@ -1186,68 +1198,43 @@ int vmac_close(struct net_device *dev) return 0; } -static void rk29_init_vmac(struct net_device *dev) +int vmac_shutdown(struct net_device *dev) { struct vmac_priv *ap = netdev_priv(dev); unsigned int temp; - struct clk *mac_clk = NULL; - struct rk29_vmac_platform_data *pdata = ap->pdev->dev.platform_data; - + printk("enter func %s...\n", __func__); - - //set rmii ref clock 50MHz - mac_clk = clk_get(NULL, "mac_ref_div"); - clk_set_rate(mac_clk, 50000000); - clk_enable(mac_clk); - clk_enable(clk_get(NULL,"mii_rx")); - clk_enable(clk_get(NULL,"mii_tx")); - clk_enable(clk_get(NULL,"hclk_mac")); - clk_enable(clk_get(NULL,"mac_ref")); - - //phy power on - if (pdata && pdata->rmii_power_control) - pdata->rmii_power_control(1); - //msleep(1000); - - /* IRQ mask */ - temp = RXINT_MASK | ERR_MASK | TXCH_MASK | MDIO_MASK; - vmac_writel(ap, temp, ENABLE); + netif_stop_queue(dev); + napi_disable(&ap->napi); - /* Set control */ - temp = (RX_BDT_LEN << 24) | (TX_BDT_LEN << 16) | TXRN_MASK | RXRN_MASK; + /* stop running transfers */ + temp = vmac_readl(ap, CONTROL); + temp &= ~(TXRN_MASK | RXRN_MASK); vmac_writel(ap, temp, CONTROL); - /* enable, after all other bits are set */ - vmac_writel(ap, temp | EN_MASK, CONTROL); + del_timer_sync(&ap->rx_timeout); -} + /* disable phy */ + phy_stop(ap->phy_dev); + vmac_mii_exit(dev); + netif_carrier_off(dev); -static void rk29_vmac_shutdown(struct net_device *dev) -{ - struct vmac_priv *ap = netdev_priv(dev); - struct rk29_vmac_platform_data *pdata = ap->pdev->dev.platform_data; - - printk("enter func %s...\n", __func__); - /* disable interrupts */ vmac_writel(ap, 0, ENABLE); - + free_irq(dev->irq, dev); + /* turn off vmac */ vmac_writel(ap, 0, CONTROL); - - //phy power off - if (pdata && pdata->rmii_power_control) - pdata->rmii_power_control(0); - - //clock close - clk_disable(clk_get(NULL, "mac_ref_div")); - clk_disable(clk_get(NULL,"mii_rx")); - clk_disable(clk_get(NULL,"mii_tx")); - clk_disable(clk_get(NULL,"hclk_mac")); - clk_disable(clk_get(NULL,"mac_ref")); -} + /* vmac_reset_hw(vmac) */ + + ap->shutdown = 1; + wmb(); + + free_buffers(dev); + return 0; +} void vmac_update_stats(struct vmac_priv *ap) { @@ -1475,7 +1462,6 @@ static int __devinit vmac_probe(struct platform_device *pdev) struct resource *res; unsigned int mem_base, mem_size, irq; int err; - struct clk *sys_clk; struct rk29_vmac_platform_data *pdata = pdev->dev.platform_data; dev = alloc_etherdev(sizeof(*ap)); @@ -1554,7 +1540,10 @@ static int __devinit vmac_probe(struct platform_device *pdev) dev->irq, dev->dev_addr); platform_set_drvdata(pdev, dev); + ap->suspending = 0; + ap->open_flag = 0; wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "vmac"); + wake_lock_init(&ap->resume_lock, WAKE_LOCK_SUSPEND, "vmac_resume"); //config rk29 vmac as rmii, 100MHz if (pdata && pdata->vmac_register_set) @@ -1608,19 +1597,42 @@ static int __devexit vmac_remove(struct platform_device *pdev) return 0; } +static void rk29_vmac_power_off(struct net_device *dev) +{ + struct vmac_priv *ap = netdev_priv(dev); + struct rk29_vmac_platform_data *pdata = ap->pdev->dev.platform_data; + + printk("enter func %s...\n", __func__); + + //phy power off + if (pdata && pdata->rmii_power_control) + pdata->rmii_power_control(0); + + //clock close + clk_disable(clk_get(NULL, "mac_ref_div")); + clk_disable(clk_get(NULL,"mii_rx")); + clk_disable(clk_get(NULL,"mii_tx")); + clk_disable(clk_get(NULL,"hclk_mac")); + clk_disable(clk_get(NULL,"mac_ref")); + +} + static int rk29_vmac_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = platform_get_drvdata(pdev); - + struct vmac_priv *ap = netdev_priv(ndev); + if (ndev) { - if (netif_running(ndev)) { - if (ndev->irq) - disable_irq(ndev->irq); + if (ap->open_flag == 1) { netif_stop_queue(ndev); netif_device_detach(ndev); - rk29_vmac_shutdown(ndev); + if (ap->suspending == 0) { + vmac_shutdown(ndev); + rk29_vmac_power_off(ndev); + ap->suspending = 1; + } } } return 0; @@ -1631,14 +1643,12 @@ rk29_vmac_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = platform_get_drvdata(pdev); - + struct vmac_priv *ap = netdev_priv(ndev); + if (ndev) { - if (netif_running(ndev)) { - rk29_init_vmac(ndev); + if (ap->open_flag == 1) { netif_device_attach(ndev); netif_start_queue(ndev); - if (ndev->irq) - enable_irq(ndev->irq); } } return 0; diff --git a/drivers/net/rk29_vmac.h b/drivers/net/rk29_vmac.h index eb962d80b549..a1f67c07fb37 100755 --- a/drivers/net/rk29_vmac.h +++ b/drivers/net/rk29_vmac.h @@ -183,6 +183,10 @@ struct vmac_priv { int speed; int duplex; + int open_flag; + int suspending; + struct wake_lock resume_lock; + /* debug */ int shutdown; }; -- 2.34.1