--- linux-2.6.22.14/drivers/net/ucc_geth.c 2008-01-22 14:03:03.000000000 +0200 +++ linux/drivers/net/ucc_geth.c 2008-01-15 11:04:02.000000000 +0200 @@ -71,7 +71,7 @@ static struct ucc_geth_info ugeth_primar .uf_info = { .bd_mem_part = MEM_PART_SYSTEM, .rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES, - .max_rx_buf_length = 1536, + .max_rx_buf_length = 1664, /* adjusted at startup if max-speed 1000 */ .urfs = UCC_GETH_URFS_INIT, .urfet = UCC_GETH_URFET_INIT, @@ -107,10 +107,10 @@ static struct ucc_geth_info ugeth_primar .maxGroupAddrInHash = 4, .maxIndAddrInHash = 4, .prel = 7, - .maxFrameLength = 1518, + .maxFrameLength = 1650, .minFrameLength = 64, - .maxD1Length = 1520, - .maxD2Length = 1520, + .maxD1Length = 1648, + .maxD2Length = 1648, .vlantype = 0x8100, .ecamptr = ((uint32_t) NULL), .eventRegMask = UCCE_OTHER, @@ -1630,6 +1630,7 @@ static int ugeth_graceful_stop_tx(struct struct ucc_fast_private *uccf; u32 cecr_subblock; u32 temp; + unsigned i; uccf = ugeth->uccf; @@ -1646,9 +1647,10 @@ static int ugeth_graceful_stop_tx(struct QE_CR_PROTOCOL_ETHERNET, 0); /* Wait for command to complete */ - do { + for (i = 0; i < 100000; ++i) { temp = in_be32(uccf->p_ucce); - } while (!(temp & UCCE_GRA)); + if (temp & UCCE_GRA) break; + } uccf->stopped_tx = 1; @@ -1660,6 +1662,7 @@ static int ugeth_graceful_stop_rx(struct struct ucc_fast_private *uccf; u32 cecr_subblock; u8 temp; + unsigned i; uccf = ugeth->uccf; @@ -1670,7 +1673,7 @@ static int ugeth_graceful_stop_rx(struct /* Keep issuing command and checking acknowledge bit until it is asserted, according to spec */ - do { + for (i = 0; i < 100000; ++i) { /* Issue host command */ cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info. @@ -1679,7 +1682,8 @@ static int ugeth_graceful_stop_rx(struct QE_CR_PROTOCOL_ETHERNET, 0); temp = ugeth->p_rx_glbl_pram->rxgstpack; - } while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX)); + if (temp & GRACEFUL_STOP_ACKNOWLEDGE_RX) break; + } uccf->stopped_rx = 1; @@ -2222,7 +2226,7 @@ static void ucc_geth_set_multi(struct ne (struct ucc_geth_82xx_address_filtering_pram *) ugeth-> p_rx_glbl_pram->addressfiltering; - if (dev->flags & IFF_ALLMULTI) { + if (1 /*dev->flags & IFF_ALLMULTI */) { /* Catch all multicast addresses, so set the * filter to all 1's. */ @@ -2261,6 +2265,39 @@ static void ucc_geth_set_multi(struct ne } } +static void ucc_geth_fast_stop(struct ucc_geth_private *ugeth) { + struct ucc_geth *ug_regs = ugeth->ug_regs; + + ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); + + out_be32(&ug_regs->maccfg1, + in_be32(&ug_regs->maccfg1) & + ~(MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX)); +} + +static void ucc_geth_fast_start(struct ucc_geth_private *ugeth) { + struct ucc_geth *ug_regs = ugeth->ug_regs; + + ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); + + out_be32(&ug_regs->maccfg1, + in_be32(&ug_regs->maccfg1) | + MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX); +} + +static void ucc_geth_fast_restart(struct ucc_geth_private *ugeth) { + ucc_geth_fast_stop(ugeth); + ucc_geth_fast_start(ugeth); +} + +static void ucc_geth_tx_check(struct ucc_geth_private *ugeth) { + if (ugeth->last_tx && jiffies - ugeth->last_tx > HZ) { + ugeth->last_tx = 0; + printk("%s: tx is stuck, restart\n", ugeth->dev->name); + ucc_geth_fast_restart(ugeth); + } +} + static void ucc_geth_stop(struct ucc_geth_private *ugeth) { struct ucc_geth *ug_regs = ugeth->ug_regs; @@ -3319,12 +3356,7 @@ static void ucc_geth_timeout(struct net_ ugeth_dump_regs(ugeth); - if (dev->flags & IFF_UP) { - ucc_geth_stop(ugeth); - ucc_geth_startup(ugeth); - } - - netif_schedule(dev); + ucc_geth_fast_restart(ugeth); } /* This is called by the kernel when a frame is ready for transmission. */ @@ -3366,8 +3398,11 @@ static int ucc_geth_start_xmit(struct sk /* set bd status and length */ out_be32((u32 *)bd, bd_status); + out_be16(&((struct ucc_fast *)ugeth->ug_regs)->utodr, 0xffff); dev->trans_start = jiffies; + if (!ugeth->last_tx) ugeth->last_tx = jiffies; + else ucc_geth_tx_check(ugeth); /* Move to next BD in the ring */ if (!(bd_status & T_W)) @@ -3492,20 +3527,23 @@ static int ucc_geth_tx(struct net_device bd = ugeth->confBd[txQ]; bd_status = in_be32((u32 *)bd); + ucc_geth_tx_check(ugeth); + /* Normal processing. */ while ((bd_status & T_R) == 0) { /* BD contains already transmitted buffer. */ /* Handle the transmitted buffer and release */ /* the BD to be used with the current frame */ + ugeth->last_tx = 0; + if ((bd == ugeth->txBd[txQ]) && (netif_queue_stopped(dev) == 0)) break; ugeth->stats.tx_packets++; /* Free the sk buffer associated with this TxBD */ - dev_kfree_skb_irq(ugeth-> - tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]]); + dev_kfree_skb(ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]]); ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]] = NULL; ugeth->skb_dirtytx[txQ] = (ugeth->skb_dirtytx[txQ] + @@ -3526,17 +3564,25 @@ static int ucc_geth_tx(struct net_device return 0; } +#define UCCE_TXRX (UCCE_RXF0 | UCCE_TXB0 | UCCE_BSY | UCCE_OTHER) + #ifdef CONFIG_UGETH_NAPI static int ucc_geth_poll(struct net_device *dev, int *budget) { struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_geth_info *ug_info; - struct ucc_fast_private *uccf; + struct ucc_fast_private *uccf = ugeth->uccf; int howmany; u8 i; int rx_work_limit; register u32 uccm; + out_be32(uccf->p_ucce, UCCE_TXRX); + + spin_lock(&ugeth->lock); + ucc_geth_tx(dev, 0); + spin_unlock(&ugeth->lock); + ug_info = ugeth->ug_info; rx_work_limit = *budget; @@ -3553,15 +3599,16 @@ static int ucc_geth_poll(struct net_devi rx_work_limit -= howmany; *budget -= howmany; - if (rx_work_limit > 0) { + if (rx_work_limit < 0) + return 1; + + if (in_be32(uccf->p_ucce) & UCCE_TXRX) + return 1; + netif_rx_complete(dev); - uccf = ugeth->uccf; - uccm = in_be32(uccf->p_uccm); - uccm |= UCCE_RX_EVENTS; - out_be32(uccf->p_uccm, uccm); - } + out_be32(uccf->p_uccm, in_be32(uccf->p_uccm) | UCCE_TXRX); - return (rx_work_limit > 0) ? 0 : 1; + return 0; } #endif /* CONFIG_UGETH_NAPI */ @@ -3587,6 +3634,14 @@ static irqreturn_t ucc_geth_irq_handler( uccf = ugeth->uccf; ug_info = ugeth->ug_info; +#ifdef CONFIG_UGETH_NAPI + ucce = in_be32(uccf->p_ucce) & in_be32(uccf->p_uccm); + if (!ucce) + return IRQ_NONE; + + out_be32(uccf->p_uccm, in_be32(uccf->p_uccm) & ~UCCE_TXRX); + netif_rx_schedule(dev); +#else /* read and clear events */ ucce = (u32) in_be32(uccf->p_ucce); uccm = (u32) in_be32(uccf->p_uccm); @@ -3635,6 +3690,7 @@ static irqreturn_t ucc_geth_irq_handler( } } +#endif return IRQ_HANDLED; } @@ -3732,7 +3788,44 @@ static int ucc_geth_close(struct net_dev return 0; } -const struct ethtool_ops ucc_geth_ethtool_ops = { }; +static void ugeth_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { + struct ucc_geth_private *ugeth = netdev_priv(dev); + + strcpy(info->driver, "ugeth"); + strcpy(info->version, "1.0"); + sprintf(info->bus_info, "ucc.%d", + ugeth->ug_info->uf_info.ucc_num); +} + +static int ugeth_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) { + struct ucc_geth_private *ugeth = netdev_priv(dev); + struct phy_device *phydev = ugeth->phydev; + + if (NULL == phydev) + return -ENODEV; + + return phy_ethtool_gset(phydev, cmd); +} + +static int ugeth_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) { + struct ucc_geth_private *ugeth = netdev_priv(dev); + struct phy_device *phydev = ugeth->phydev; + + if (NULL == phydev) + return -ENODEV; + + return phy_ethtool_sset(phydev, cmd); +} + +const struct ethtool_ops ucc_geth_ethtool_ops = { + .get_drvinfo = ugeth_get_drvinfo, + .get_settings = ugeth_get_settings, + .set_settings = ugeth_set_settings, + .get_link = ethtool_op_get_link, +}; static phy_interface_t to_phy_interface(const char *phy_connection_type) { @@ -3754,6 +3847,15 @@ static phy_interface_t to_phy_interface( return PHY_INTERFACE_MODE_MII; } +static int ucc_geth_change_mtu(struct net_device *dev, int new_mtu) { + struct ucc_geth_private *ugeth = netdev_priv(dev); + if (new_mtu < 68 || new_mtu > 1632) { + return -EINVAL; + } + dev->mtu = new_mtu; + return 0; +} + static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; @@ -3906,7 +4008,7 @@ static int ucc_geth_probe(struct of_devi #endif /* CONFIG_UGETH_NAPI */ dev->stop = ucc_geth_close; dev->get_stats = ucc_geth_get_stats; -// dev->change_mtu = ucc_geth_change_mtu; + dev->change_mtu = ucc_geth_change_mtu; dev->mtu = 1500; dev->set_multicast_list = ucc_geth_set_multi; dev->ethtool_ops = &ucc_geth_ethtool_ops;