From: David Brownell <[EMAIL PROTECTED]> Prevent unaligned packet oops on enc28j60 packet RX.
Keep enc28j60 chips in low-power mode when they're not in use. At typically 120 mA, these chips run hot even when idle. Low power mode cuts that power usage by a factor of around 100. Signed-off-by: David Brownell <[EMAIL PROTECTED]> --- drivers/net/enc28j60.c | 54 +++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 50 insertions(+), 4 deletions(-) --- avr.orig/drivers/net/enc28j60.c 2008-02-05 10:04:22.000000000 -0800 +++ avr/drivers/net/enc28j60.c 2008-02-05 10:50:50.000000000 -0800 @@ -594,6 +594,43 @@ static void nolock_txfifo_init(struct en nolock_regw_write(priv, ETXNDL, end); } +/* + * Low power mode shrinks power consumption about 100x, so we'd like + * the chip to be in that mode whenever it's inactive. + */ +static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low) +{ + int tmp; + + dev_dbg(&priv->spi->dev, "%s power...\n", is_low ? "low" : "high"); + + mutex_lock(&priv->lock); + if (is_low) { + nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); + for (;;) { + tmp = nolock_regb_read(priv, ESTAT); + if (!(tmp & ESTAT_RXBUSY)) + break; + } + for (;;) { + tmp = nolock_regb_read(priv, ECON1); + if (!(tmp & ECON1_TXRTS)) + break; + } + /* ECON2_VRPS was set during initialization */ + nolock_reg_bfset(priv, ECON2, ECON2_PWRSV); + } else { + nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV); + for (;;) { + tmp = nolock_regb_read(priv, ESTAT); + if (tmp & ESTAT_CLKRDY) + break; + } + /* caller sets ECON1_RXEN */ + } + mutex_unlock(&priv->lock); +} + static int enc28j60_hw_init(struct enc28j60_net *priv) { u8 reg; @@ -612,8 +649,8 @@ static int enc28j60_hw_init(struct enc28 priv->tx_retry_count = 0; priv->max_pk_counter = 0; priv->rxfilter = RXFILTER_NORMAL; - /* enable address auto increment */ - nolock_regb_write(priv, ECON2, ECON2_AUTOINC); + /* enable address auto increment and voltage regulator powersave */ + nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS); nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT); nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT); @@ -690,7 +727,9 @@ static int enc28j60_hw_init(struct enc28 static void enc28j60_hw_enable(struct enc28j60_net *priv) { - /* enable interrutps */ + enc28j60_lowpower(priv, false); + + /* enable interrupts */ if (netif_msg_hw(priv)) printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n", __FUNCTION__); @@ -717,6 +756,8 @@ static void enc28j60_hw_disable(struct e nolock_reg_bfclr(priv, ECON1, ECON1_RXEN); priv->hw_enable = false; mutex_unlock(&priv->lock); + + enc28j60_lowpower(priv, true); } static int @@ -734,6 +775,8 @@ enc28j60_setlink(struct net_device *ndev "hw_reset() failed\n"); ret = -EINVAL; } + enc28j60_lowpower(priv, true); + } else { if (netif_msg_link(priv)) dev_warn(&ndev->dev, @@ -900,7 +943,7 @@ static void enc28j60_hw_rx(struct net_de if (RSV_GETBIT(rxstat, RSV_LENCHECKERR)) ndev->stats.rx_frame_errors++; } else { - skb = dev_alloc_skb(len); + skb = dev_alloc_skb(len + NET_IP_ALIGN); if (!skb) { if (netif_msg_rx_err(priv)) dev_err(&ndev->dev, @@ -908,6 +951,7 @@ static void enc28j60_hw_rx(struct net_de ndev->stats.rx_dropped++; } else { skb->dev = ndev; + skb_reserve(skb, NET_IP_ALIGN); /* copy the packet from the receive buffer */ enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(rsv), len, skb_put(skb, len)); @@ -1536,6 +1580,8 @@ static int __devinit enc28j60_probe(stru dev->watchdog_timeo = TX_TIMEOUT; SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops); + enc28j60_lowpower(priv, true); + ret = register_netdev(dev); if (ret) { if (netif_msg_probe(priv)) -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html