+ wmb();
+ flags = TXFLAG_OWN | TXFLAG_INT;
+ /*
+ * Set checksum flags while not tso
+ */
+ if (jme_tx_tso(skb, &txdesc->desc1.mss, &flags))
+ jme_tx_csum(jme, skb, &flags);
+ jme_tx_vlan(skb, &txdesc->desc1.vlan, &flags);
+ jme_map_tx_skb(jme, skb, idx);
+ txdesc->desc1.flags = flags;
+ /*
+ * Set tx buffer info after telling NIC to send
+ * For better tx_clean timing
+ */
+ wmb();
+ txbi->nr_desc = skb_shinfo(skb)->nr_frags + 2;
+ txbi->skb = skb;
+ txbi->len = skb->len;
+ txbi->start_xmit = jiffies;
+ if (!txbi->start_xmit)
+ txbi->start_xmit = (0UL-1);
+
+ return 0;
+}
+
+static void
+jme_stop_queue_if_full(struct jme_adapter *jme)
+{
+ struct jme_ring *txring = &(jme->txring[0]);
+ struct jme_buffer_info *txbi = txring->bufinf;
+ int idx = atomic_read(&txring->next_to_clean);
+
+ txbi += idx;
+
+ smp_wmb();
+ if (unlikely(atomic_read(&txring->nr_free) < (MAX_SKB_FRAGS+2))) {
+ netif_stop_queue(jme->dev);
+ netif_info(jme, tx_queued, jme->dev, "TX Queue Paused\n");
+ smp_wmb();
+ if (atomic_read(&txring->nr_free)
+ >= (jme->tx_wake_threshold)) {
+ netif_wake_queue(jme->dev);
+ netif_info(jme, tx_queued, jme->dev, "TX Queue Fast Waked\n");
+ }
+ }
+
+ if (unlikely(txbi->start_xmit &&
+ (jiffies - txbi->start_xmit) >= TX_TIMEOUT &&
+ txbi->skb)) {
+ netif_stop_queue(jme->dev);
+ netif_info(jme, tx_queued, jme->dev, "TX Queue Stopped %d@%lu\n", idx, jiffies);
+ }
+}
+
+/*
+ * This function is already protected by netif_tx_lock()
+ */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,31)
+static int
+#else
+static netdev_tx_t
+#endif
+jme_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ int idx;
+
+ if (unlikely(jme_expand_header(jme, skb))) {
+ ++(NET_STAT(jme).tx_dropped);
+ return NETDEV_TX_OK;
+ }
+
+ idx = jme_alloc_txdesc(jme, skb);
+
+ if (unlikely(idx < 0)) {
+ netif_stop_queue(netdev);
+ netif_err(jme, tx_err, jme->dev,
+ "BUG! Tx ring full when queue awake!\n");
+
+ return NETDEV_TX_BUSY;
+ }
+
+ jme_fill_tx_desc(jme, skb, idx);
+
+ jwrite32(jme, JME_TXCS, jme->reg_txcs |
+ TXCS_SELECT_QUEUE0 |
+ TXCS_QUEUE0S |
+ TXCS_ENABLE);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,29)
+ netdev->trans_start = jiffies;
+#endif
+
+ tx_dbg(jme, "xmit: %d+%d@%lu\n",
+ idx, skb_shinfo(skb)->nr_frags + 2, jiffies);
+ jme_stop_queue_if_full(jme);
+
+ return NETDEV_TX_OK;
+}
+
+static int
+jme_set_macaddr(struct net_device *netdev, void *p)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+ u32 val;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ spin_lock_bh(&jme->macaddr_lock);
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+
+ val = (addr->sa_data[3] & 0xff) << 24 |
+ (addr->sa_data[2] & 0xff) << 16 |
+ (addr->sa_data[1] & 0xff) << 8 |
+ (addr->sa_data[0] & 0xff);
+ jwrite32(jme, JME_RXUMA_LO, val);
+ val = (addr->sa_data[5] & 0xff) << 8 |
+ (addr->sa_data[4] & 0xff);
+ jwrite32(jme, JME_RXUMA_HI, val);
+ spin_unlock_bh(&jme->macaddr_lock);
+
+ return 0;
+}
+
+static void
+jme_set_multi(struct net_device *netdev)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ u32 mc_hash[2] = {};
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,33)
+ int i;
+#endif
+
+ spin_lock_bh(&jme->rxmcs_lock);
+
+ jme->reg_rxmcs |= RXMCS_BRDFRAME | RXMCS_UNIFRAME;
+
+ if (netdev->flags & IFF_PROMISC) {
+ jme->reg_rxmcs |= RXMCS_ALLFRAME;
+ } else if (netdev->flags & IFF_ALLMULTI) {
+ jme->reg_rxmcs |= RXMCS_ALLMULFRAME;
+ } else if (netdev->flags & IFF_MULTICAST) {
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
+ struct dev_mc_list *mclist;
+#else
+ struct netdev_hw_addr *ha;
+#endif
+ int bit_nr;
+
+ jme->reg_rxmcs |= RXMCS_MULFRAME | RXMCS_MULFILTERED;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,33)
+ for (i = 0, mclist = netdev->mc_list;
+ mclist && i < netdev->mc_count;
+ ++i, mclist = mclist->next) {
+#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
+ netdev_for_each_mc_addr(mclist, netdev) {
+#else
+ netdev_for_each_mc_addr(ha, netdev) {
+#endif
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,34)
+ bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x3F;
+#else
+ bit_nr = ether_crc(ETH_ALEN, ha->addr) & 0x3F;
+#endif
+ mc_hash[bit_nr >> 5] |= 1 << (bit_nr & 0x1F);
+ }
+
+ jwrite32(jme, JME_RXMCHT_LO, mc_hash[0]);
+ jwrite32(jme, JME_RXMCHT_HI, mc_hash[1]);
+ }
+
+ wmb();
+ jwrite32(jme, JME_RXMCS, jme->reg_rxmcs);
+
+ spin_unlock_bh(&jme->rxmcs_lock);
+}
+
+static int
+jme_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ if (new_mtu == jme->old_mtu)
+ return 0;
+
+ if (((new_mtu + ETH_HLEN) > MAX_ETHERNET_JUMBO_PACKET_SIZE) ||
+ ((new_mtu) < IPV6_MIN_MTU))
+ return -EINVAL;
+
+ if (new_mtu > 4000) {
+ jme->reg_rxcs &= ~RXCS_FIFOTHNP;
+ jme->reg_rxcs |= RXCS_FIFOTHNP_64QW;
+ jme_restart_rx_engine(jme);
+ } else {
+ jme->reg_rxcs &= ~RXCS_FIFOTHNP;
+ jme->reg_rxcs |= RXCS_FIFOTHNP_128QW;
+ jme_restart_rx_engine(jme);
+ }
+
+ if (new_mtu > 1900) {
+ netdev->features &= ~(NETIF_F_HW_CSUM |
+ NETIF_F_TSO
+#ifdef NETIF_F_TSO6
+ | NETIF_F_TSO6
+#endif
+ );
+ } else {
+ if (test_bit(JME_FLAG_TXCSUM, &jme->flags))
+ netdev->features |= NETIF_F_HW_CSUM;
+ if (test_bit(JME_FLAG_TSO, &jme->flags))
+ netdev->features |= NETIF_F_TSO
+#ifdef NETIF_F_TSO6
+ | NETIF_F_TSO6
+#endif
+ ;
+ }
+
+ netdev->mtu = new_mtu;
+ jme_reset_link(jme);
+
+ return 0;
+}
+
+static void
+jme_tx_timeout(struct net_device *netdev)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ jme->phylink = 0;
+ jme_reset_phy_processor(jme);
+ if (test_bit(JME_FLAG_SSET, &jme->flags))
+ jme_set_settings(netdev, &jme->old_ecmd);
+
+ /*
+ * Force to Reset the link again
+ */
+ jme_reset_link(jme);
+}
+
+static inline void jme_pause_rx(struct jme_adapter *jme)
+{
+ atomic_dec(&jme->link_changing);
+
+ jme_set_rx_pcc(jme, PCC_OFF);
+ if (test_bit(JME_FLAG_POLL, &jme->flags)) {
+ JME_NAPI_DISABLE(jme);
+ } else {
+ tasklet_disable(&jme->rxclean_task);
+ tasklet_disable(&jme->rxempty_task);
+ }
+}
+
+static inline void jme_resume_rx(struct jme_adapter *jme)
+{
+ struct dynpcc_info *dpi = &(jme->dpi);
+
+ if (test_bit(JME_FLAG_POLL, &jme->flags)) {
+ JME_NAPI_ENABLE(jme);
+ } else {
+ tasklet_hi_enable(&jme->rxclean_task);
+ tasklet_hi_enable(&jme->rxempty_task);
+ }
+ dpi->cur = PCC_P1;
+ dpi->attempt = PCC_P1;
+ dpi->cnt = 0;
+ jme_set_rx_pcc(jme, PCC_P1);
+
+ atomic_inc(&jme->link_changing);
+}
+
+static void
+jme_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ jme_pause_rx(jme);
+ jme->vlgrp = grp;
+ jme_resume_rx(jme);
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21)
+static void
+jme_vlan_rx_kill_vid(struct net_device *netdev, unsigned short vid)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ if(jme->vlgrp) {
+ jme_pause_rx(jme);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,20)
+ jme->vlgrp->vlan_devices[vid] = NULL;
+#else
+ vlan_group_set_device(jme->vlgrp, vid, NULL);
+#endif
+ jme_resume_rx(jme);
+ }
+}
+#endif
+
+static void
+jme_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(jme->pdev));
+}
+
+static int
+jme_get_regs_len(struct net_device *netdev)
+{
+ return JME_REG_LEN;
+}
+
+static void
+mmapio_memcpy(struct jme_adapter *jme, u32 *p, u32 reg, int len)
+{
+ int i;
+
+ for (i = 0 ; i < len ; i += 4)
+ p[i >> 2] = jread32(jme, reg + i);
+}
+
+static void
+mdio_memcpy(struct jme_adapter *jme, u32 *p, int reg_nr)
+{
+ int i;
+ u16 *p16 = (u16 *)p;
+
+ for (i = 0 ; i < reg_nr ; ++i)
+ p16[i] = jme_mdio_read(jme->dev, jme->mii_if.phy_id, i);
+}
+
+static void
+jme_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ u32 *p32 = (u32 *)p;
+
+ memset(p, 0xFF, JME_REG_LEN);
+
+ regs->version = 1;
+ mmapio_memcpy(jme, p32, JME_MAC, JME_MAC_LEN);
+
+ p32 += 0x100 >> 2;
+ mmapio_memcpy(jme, p32, JME_PHY, JME_PHY_LEN);
+
+ p32 += 0x100 >> 2;
+ mmapio_memcpy(jme, p32, JME_MISC, JME_MISC_LEN);
+
+ p32 += 0x100 >> 2;
+ mmapio_memcpy(jme, p32, JME_RSS, JME_RSS_LEN);
+
+ p32 += 0x100 >> 2;
+ mdio_memcpy(jme, p32, JME_PHY_REG_NR);
+}
+
+static int
+jme_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ ecmd->tx_coalesce_usecs = PCC_TX_TO;
+ ecmd->tx_max_coalesced_frames = PCC_TX_CNT;
+
+ if (test_bit(JME_FLAG_POLL, &jme->flags)) {
+ ecmd->use_adaptive_rx_coalesce = false;
+ ecmd->rx_coalesce_usecs = 0;
+ ecmd->rx_max_coalesced_frames = 0;
+ return 0;
+ }
+
+ ecmd->use_adaptive_rx_coalesce = true;
+
+ switch (jme->dpi.cur) {
+ case PCC_P1:
+ ecmd->rx_coalesce_usecs = PCC_P1_TO;
+ ecmd->rx_max_coalesced_frames = PCC_P1_CNT;
+ break;
+ case PCC_P2:
+ ecmd->rx_coalesce_usecs = PCC_P2_TO;
+ ecmd->rx_max_coalesced_frames = PCC_P2_CNT;
+ break;
+ case PCC_P3:
+ ecmd->rx_coalesce_usecs = PCC_P3_TO;
+ ecmd->rx_max_coalesced_frames = PCC_P3_CNT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ struct dynpcc_info *dpi = &(jme->dpi);
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ if (ecmd->use_adaptive_rx_coalesce &&
+ test_bit(JME_FLAG_POLL, &jme->flags)) {
+ clear_bit(JME_FLAG_POLL, &jme->flags);
+ jme->jme_rx = netif_rx;
+ jme->jme_vlan_rx = vlan_hwaccel_rx;
+ dpi->cur = PCC_P1;
+ dpi->attempt = PCC_P1;
+ dpi->cnt = 0;
+ jme_set_rx_pcc(jme, PCC_P1);
+ jme_interrupt_mode(jme);
+ } else if (!(ecmd->use_adaptive_rx_coalesce) &&
+ !(test_bit(JME_FLAG_POLL, &jme->flags))) {
+ set_bit(JME_FLAG_POLL, &jme->flags);
+ jme->jme_rx = netif_receive_skb;
+ jme->jme_vlan_rx = vlan_hwaccel_receive_skb;
+ jme_interrupt_mode(jme);
+ }
+
+ return 0;
+}
+
+static void
+jme_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *ecmd)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ u32 val;
+
+ ecmd->tx_pause = (jme->reg_txpfc & TXPFC_PF_EN) != 0;
+ ecmd->rx_pause = (jme->reg_rxmcs & RXMCS_FLOWCTRL) != 0;
+
+ spin_lock_bh(&jme->phy_lock);
+ val = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_ADVERTISE);
+ spin_unlock_bh(&jme->phy_lock);
+
+ ecmd->autoneg =
+ (val & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) != 0;
+}
+
+static int
+jme_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *ecmd)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ u32 val;
+
+ if (((jme->reg_txpfc & TXPFC_PF_EN) != 0) ^
+ (ecmd->tx_pause != 0)) {
+
+ if (ecmd->tx_pause)
+ jme->reg_txpfc |= TXPFC_PF_EN;
+ else
+ jme->reg_txpfc &= ~TXPFC_PF_EN;
+
+ jwrite32(jme, JME_TXPFC, jme->reg_txpfc);
+ }
+
+ spin_lock_bh(&jme->rxmcs_lock);
+ if (((jme->reg_rxmcs & RXMCS_FLOWCTRL) != 0) ^
+ (ecmd->rx_pause != 0)) {
+
+ if (ecmd->rx_pause)
+ jme->reg_rxmcs |= RXMCS_FLOWCTRL;
+ else
+ jme->reg_rxmcs &= ~RXMCS_FLOWCTRL;
+
+ jwrite32(jme, JME_RXMCS, jme->reg_rxmcs);
+ }
+ spin_unlock_bh(&jme->rxmcs_lock);
+
+ spin_lock_bh(&jme->phy_lock);
+ val = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_ADVERTISE);
+ if (((val & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) != 0) ^
+ (ecmd->autoneg != 0)) {
+
+ if (ecmd->autoneg)
+ val |= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+ else
+ val &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+ jme_mdio_write(jme->dev, jme->mii_if.phy_id,
+ MII_ADVERTISE, val);
+ }
+ spin_unlock_bh(&jme->phy_lock);
+
+ return 0;
+}
+
+static void
+jme_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ wol->supported = WAKE_MAGIC | WAKE_PHY;
+
+ wol->wolopts = 0;
+
+ if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
+ wol->wolopts |= WAKE_PHY;
+
+ if (jme->reg_pmcs & PMCS_MFEN)
+ wol->wolopts |= WAKE_MAGIC;
+
+}
+
+static int
+jme_set_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ if (wol->wolopts & (WAKE_MAGICSECURE |
+ WAKE_UCAST |
+ WAKE_MCAST |
+ WAKE_BCAST |
+ WAKE_ARP))
+ return -EOPNOTSUPP;
+
+ jme->reg_pmcs = 0;
+
+ if (wol->wolopts & WAKE_PHY)
+ jme->reg_pmcs |= PMCS_LFEN | PMCS_LREN;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ jme->reg_pmcs |= PMCS_MFEN;
+
+ jwrite32(jme, JME_PMCS, jme->reg_pmcs);
+
+ return 0;
+}
+
+static int
+jme_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ int rc;
+
+ spin_lock_bh(&jme->phy_lock);
+ rc = mii_ethtool_gset(&(jme->mii_if), ecmd);
+ spin_unlock_bh(&jme->phy_lock);
+ return rc;
+}
+
+static int
+jme_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct jme_adapter *jme = netdev_priv(netdev);
+ int rc, fdc = 0;
+
+ if (ecmd->speed == SPEED_1000 && ecmd->autoneg != AUTONEG_ENABLE)
+ return -EINVAL;
+
+ /*
+ * Check If user changed duplex only while force_media.
+ * Hardware would not generate link change interrupt.
+ */
+ if (jme->mii_if.force_media &&
+ ecmd->autoneg != AUTONEG_ENABLE &&
+ (jme->mii_if.full_duplex != ecmd->duplex))
+ fdc = 1;
+
+ spin_lock_bh(&jme->phy_lock);
+ rc = mii_ethtool_sset(&(jme->mii_if), ecmd);
+ spin_unlock_bh(&jme->phy_lock);