diff options
| -rw-r--r-- | drivers/net/enc28j60.c | 82 |
1 files changed, 58 insertions, 24 deletions
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index 0f1581cbd101..c05cb159c772 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c | |||
| @@ -400,26 +400,31 @@ enc28j60_packet_write(struct enc28j60_net *priv, int len, const u8 *data) | |||
| 400 | mutex_unlock(&priv->lock); | 400 | mutex_unlock(&priv->lock); |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | /* | 403 | static unsigned long msec20_to_jiffies; |
| 404 | * Wait until the PHY operation is complete. | 404 | |
| 405 | */ | 405 | static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val) |
| 406 | static int wait_phy_ready(struct enc28j60_net *priv) | ||
| 407 | { | 406 | { |
| 408 | unsigned long timeout = jiffies + 20 * HZ / 1000; | 407 | unsigned long timeout = jiffies + msec20_to_jiffies; |
| 409 | int ret = 1; | ||
| 410 | 408 | ||
| 411 | /* 20 msec timeout read */ | 409 | /* 20 msec timeout read */ |
| 412 | while (nolock_regb_read(priv, MISTAT) & MISTAT_BUSY) { | 410 | while ((nolock_regb_read(priv, reg) & mask) != val) { |
| 413 | if (time_after(jiffies, timeout)) { | 411 | if (time_after(jiffies, timeout)) { |
| 414 | if (netif_msg_drv(priv)) | 412 | if (netif_msg_drv(priv)) |
| 415 | printk(KERN_DEBUG DRV_NAME | 413 | dev_dbg(&priv->spi->dev, |
| 416 | ": PHY ready timeout!\n"); | 414 | "reg %02x ready timeout!\n", reg); |
| 417 | ret = 0; | 415 | return -ETIMEDOUT; |
| 418 | break; | ||
| 419 | } | 416 | } |
| 420 | cpu_relax(); | 417 | cpu_relax(); |
| 421 | } | 418 | } |
| 422 | return ret; | 419 | return 0; |
| 420 | } | ||
| 421 | |||
| 422 | /* | ||
| 423 | * Wait until the PHY operation is complete. | ||
| 424 | */ | ||
| 425 | static int wait_phy_ready(struct enc28j60_net *priv) | ||
| 426 | { | ||
| 427 | return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1; | ||
| 423 | } | 428 | } |
| 424 | 429 | ||
| 425 | /* | 430 | /* |
| @@ -594,6 +599,32 @@ static void nolock_txfifo_init(struct enc28j60_net *priv, u16 start, u16 end) | |||
| 594 | nolock_regw_write(priv, ETXNDL, end); | 599 | nolock_regw_write(priv, ETXNDL, end); |
| 595 | } | 600 | } |
| 596 | 601 | ||
| 602 | /* | ||
| 603 | * Low power mode shrinks power consumption about 100x, so we'd like | ||
| 604 | * the chip to be in that mode whenever it's inactive. (However, we | ||
| 605 | * can't stay in lowpower mode during suspend with WOL active.) | ||
| 606 | */ | ||
| 607 | static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low) | ||
| 608 | { | ||
| 609 | if (netif_msg_drv(priv)) | ||
| 610 | dev_dbg(&priv->spi->dev, "%s power...\n", | ||
| 611 | is_low ? "low" : "high"); | ||
| 612 | |||
| 613 | mutex_lock(&priv->lock); | ||
| 614 | if (is_low) { | ||
| 615 | nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); | ||
| 616 | poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0); | ||
| 617 | poll_ready(priv, ECON1, ECON1_TXRTS, 0); | ||
| 618 | /* ECON2_VRPS was set during initialization */ | ||
| 619 | nolock_reg_bfset(priv, ECON2, ECON2_PWRSV); | ||
| 620 | } else { | ||
| 621 | nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV); | ||
| 622 | poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY); | ||
| 623 | /* caller sets ECON1_RXEN */ | ||
| 624 | } | ||
| 625 | mutex_unlock(&priv->lock); | ||
| 626 | } | ||
| 627 | |||
| 597 | static int enc28j60_hw_init(struct enc28j60_net *priv) | 628 | static int enc28j60_hw_init(struct enc28j60_net *priv) |
| 598 | { | 629 | { |
| 599 | u8 reg; | 630 | u8 reg; |
| @@ -612,8 +643,8 @@ static int enc28j60_hw_init(struct enc28j60_net *priv) | |||
| 612 | priv->tx_retry_count = 0; | 643 | priv->tx_retry_count = 0; |
| 613 | priv->max_pk_counter = 0; | 644 | priv->max_pk_counter = 0; |
| 614 | priv->rxfilter = RXFILTER_NORMAL; | 645 | priv->rxfilter = RXFILTER_NORMAL; |
| 615 | /* enable address auto increment */ | 646 | /* enable address auto increment and voltage regulator powersave */ |
| 616 | nolock_regb_write(priv, ECON2, ECON2_AUTOINC); | 647 | nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS); |
| 617 | 648 | ||
| 618 | nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT); | 649 | nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT); |
| 619 | nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT); | 650 | nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT); |
| @@ -690,7 +721,7 @@ static int enc28j60_hw_init(struct enc28j60_net *priv) | |||
| 690 | 721 | ||
| 691 | static void enc28j60_hw_enable(struct enc28j60_net *priv) | 722 | static void enc28j60_hw_enable(struct enc28j60_net *priv) |
| 692 | { | 723 | { |
| 693 | /* enable interrutps */ | 724 | /* enable interrupts */ |
| 694 | if (netif_msg_hw(priv)) | 725 | if (netif_msg_hw(priv)) |
| 695 | printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n", | 726 | printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n", |
| 696 | __FUNCTION__); | 727 | __FUNCTION__); |
| @@ -726,15 +757,12 @@ enc28j60_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex) | |||
| 726 | int ret = 0; | 757 | int ret = 0; |
| 727 | 758 | ||
| 728 | if (!priv->hw_enable) { | 759 | if (!priv->hw_enable) { |
| 729 | if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) { | 760 | /* link is in low power mode now; duplex setting |
| 761 | * will take effect on next enc28j60_hw_init(). | ||
| 762 | */ | ||
| 763 | if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) | ||
| 730 | priv->full_duplex = (duplex == DUPLEX_FULL); | 764 | priv->full_duplex = (duplex == DUPLEX_FULL); |
| 731 | if (!enc28j60_hw_init(priv)) { | 765 | else { |
| 732 | if (netif_msg_drv(priv)) | ||
| 733 | dev_err(&ndev->dev, | ||
| 734 | "hw_reset() failed\n"); | ||
| 735 | ret = -EINVAL; | ||
| 736 | } | ||
| 737 | } else { | ||
| 738 | if (netif_msg_link(priv)) | 766 | if (netif_msg_link(priv)) |
| 739 | dev_warn(&ndev->dev, | 767 | dev_warn(&ndev->dev, |
| 740 | "unsupported link setting\n"); | 768 | "unsupported link setting\n"); |
| @@ -1307,7 +1335,8 @@ static int enc28j60_net_open(struct net_device *dev) | |||
| 1307 | } | 1335 | } |
| 1308 | return -EADDRNOTAVAIL; | 1336 | return -EADDRNOTAVAIL; |
| 1309 | } | 1337 | } |
| 1310 | /* Reset the hardware here */ | 1338 | /* Reset the hardware here (and take it out of low power mode) */ |
| 1339 | enc28j60_lowpower(priv, false); | ||
| 1311 | enc28j60_hw_disable(priv); | 1340 | enc28j60_hw_disable(priv); |
| 1312 | if (!enc28j60_hw_init(priv)) { | 1341 | if (!enc28j60_hw_init(priv)) { |
| 1313 | if (netif_msg_ifup(priv)) | 1342 | if (netif_msg_ifup(priv)) |
| @@ -1337,6 +1366,7 @@ static int enc28j60_net_close(struct net_device *dev) | |||
| 1337 | printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __FUNCTION__); | 1366 | printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __FUNCTION__); |
| 1338 | 1367 | ||
| 1339 | enc28j60_hw_disable(priv); | 1368 | enc28j60_hw_disable(priv); |
| 1369 | enc28j60_lowpower(priv, true); | ||
| 1340 | netif_stop_queue(dev); | 1370 | netif_stop_queue(dev); |
| 1341 | 1371 | ||
| 1342 | return 0; | 1372 | return 0; |
| @@ -1537,6 +1567,8 @@ static int __devinit enc28j60_probe(struct spi_device *spi) | |||
| 1537 | dev->watchdog_timeo = TX_TIMEOUT; | 1567 | dev->watchdog_timeo = TX_TIMEOUT; |
| 1538 | SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops); | 1568 | SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops); |
| 1539 | 1569 | ||
| 1570 | enc28j60_lowpower(priv, true); | ||
| 1571 | |||
| 1540 | ret = register_netdev(dev); | 1572 | ret = register_netdev(dev); |
| 1541 | if (ret) { | 1573 | if (ret) { |
| 1542 | if (netif_msg_probe(priv)) | 1574 | if (netif_msg_probe(priv)) |
| @@ -1581,6 +1613,8 @@ static struct spi_driver enc28j60_driver = { | |||
| 1581 | 1613 | ||
| 1582 | static int __init enc28j60_init(void) | 1614 | static int __init enc28j60_init(void) |
| 1583 | { | 1615 | { |
| 1616 | msec20_to_jiffies = msecs_to_jiffies(20); | ||
| 1617 | |||
| 1584 | return spi_register_driver(&enc28j60_driver); | 1618 | return spi_register_driver(&enc28j60_driver); |
| 1585 | } | 1619 | } |
| 1586 | 1620 | ||
