+ if(unlikely(desccnt > 1 ||
+ rxdesc->descwb.errstat & RXWBERR_ALLERR)) {
+
+ if(rxdesc->descwb.errstat & RXWBERR_CRCERR)
+ ++(NET_STAT(jme).rx_crc_errors);
+ else if(rxdesc->descwb.errstat & RXWBERR_OVERUN)
+ ++(NET_STAT(jme).rx_fifo_errors);
+ else
+ ++(NET_STAT(jme).rx_errors);
+
+ if(desccnt > 1) {
+ rx_dbg(jme->dev->name,
+ "RX: More than one(%d) descriptor, "
+ "framelen=%d\n",
+ desccnt, le16_to_cpu(rxdesc->descwb.framesize));
+ limit -= desccnt - 1;
+ }
+
+ for(j = i, ccnt = desccnt ; ccnt-- ; ) {
+ jme_set_clean_rxdesc(jme, j);
+ j = (j + 1) & (mask);
+ }
+
+ }
+ else {
+ jme_alloc_and_feed_skb(jme, i);
+ }
+
+ i = (i + desccnt) & (mask);
+ }
+
+
+out:
+ rx_dbg(jme->dev->name, "RX: Stop at %d\n", i);
+ rx_dbg(jme->dev->name, "RX: RXNDA offset %d\n",
+ (jread32(jme, JME_RXNDA) - jread32(jme, JME_RXDBA_LO))
+ >> 4);
+
+ atomic_set(&rxring->next_to_clean, i);
+
+out_inc:
+ atomic_inc(&jme->rx_cleaning);
+
+ return limit > 0 ? limit : 0;
+
+}
+
+static void
+jme_attempt_pcc(struct dynpcc_info *dpi, int atmp)
+{
+ if(likely(atmp == dpi->cur)) {
+ dpi->cnt = 0;
+ return;
+ }
+
+ if(dpi->attempt == atmp) {
+ ++(dpi->cnt);
+ }
+ else {
+ dpi->attempt = atmp;
+ dpi->cnt = 0;
+ }
+
+}
+
+static void
+jme_dynamic_pcc(struct jme_adapter *jme)
+{
+ register struct dynpcc_info *dpi = &(jme->dpi);
+
+ if((NET_STAT(jme).rx_bytes - dpi->last_bytes) > PCC_P3_THRESHOLD)
+ jme_attempt_pcc(dpi, PCC_P3);
+ else if((NET_STAT(jme).rx_packets - dpi->last_pkts) > PCC_P2_THRESHOLD
+ || dpi->intr_cnt > PCC_INTR_THRESHOLD)
+ jme_attempt_pcc(dpi, PCC_P2);
+ else
+ jme_attempt_pcc(dpi, PCC_P1);
+
+ if(unlikely(dpi->attempt != dpi->cur && dpi->cnt > 5)) {
+ jme_set_rx_pcc(jme, dpi->attempt);
+ dpi->cur = dpi->attempt;
+ dpi->cnt = 0;
+ }
+}
+
+static void
+jme_start_pcc_timer(struct jme_adapter *jme)
+{
+ struct dynpcc_info *dpi = &(jme->dpi);
+ dpi->last_bytes = NET_STAT(jme).rx_bytes;
+ dpi->last_pkts = NET_STAT(jme).rx_packets;
+ dpi->intr_cnt = 0;
+ jwrite32(jme, JME_TMCSR,
+ TMCSR_EN | ((0xFFFFFF - PCC_INTERVAL_US) & TMCSR_CNT));
+}
+
+__always_inline static void
+jme_stop_pcc_timer(struct jme_adapter *jme)
+{
+ jwrite32(jme, JME_TMCSR, 0);
+}
+
+static void
+jme_pcc_tasklet(unsigned long arg)
+{
+ struct jme_adapter *jme = (struct jme_adapter*)arg;
+ struct net_device *netdev = jme->dev;
+
+ if(unlikely(!netif_carrier_ok(netdev) ||
+ (atomic_read(&jme->link_changing) != 1)
+ )) {
+ jme_stop_pcc_timer(jme);
+ return;
+ }
+
+ if(!(jme->flags & JME_FLAG_POLL))
+ jme_dynamic_pcc(jme);
+
+ jme_start_pcc_timer(jme);
+}
+
+__always_inline static void
+jme_polling_mode(struct jme_adapter *jme)
+{
+ jme_set_rx_pcc(jme, PCC_OFF);
+}
+
+__always_inline static void
+jme_interrupt_mode(struct jme_adapter *jme)
+{
+ jme_set_rx_pcc(jme, PCC_P1);
+}
+
+static void
+jme_link_change_tasklet(unsigned long arg)
+{
+ struct jme_adapter *jme = (struct jme_adapter*)arg;
+ struct net_device *netdev = jme->dev;
+ int timeout = WAIT_TASKLET_TIMEOUT;
+ int rc;
+
+ if(!atomic_dec_and_test(&jme->link_changing))
+ goto out;
+
+ if(jme_check_link(netdev, 1) && jme->old_mtu == netdev->mtu)
+ goto out;
+
+ jme->old_mtu = netdev->mtu;
+ netif_stop_queue(netdev);
+
+ while(--timeout > 0 &&
+ (
+ atomic_read(&jme->rx_cleaning) != 1 ||
+ atomic_read(&jme->tx_cleaning) != 1
+ )) {
+
+ mdelay(1);
+ }
+
+ if(netif_carrier_ok(netdev)) {
+ jme_stop_pcc_timer(jme);
+ jme_reset_mac_processor(jme);
+ jme_free_rx_resources(jme);
+ jme_free_tx_resources(jme);
+
+ if(jme->flags & JME_FLAG_POLL)
+ jme_polling_mode(jme);
+ }
+
+ jme_check_link(netdev, 0);
+ if(netif_carrier_ok(netdev)) {
+ rc = jme_setup_rx_resources(jme);
+ if(rc) {
+ jeprintk(netdev->name,
+ "Allocating resources for RX error"
+ ", Device STOPPED!\n");
+ goto out;
+ }
+
+
+ rc = jme_setup_tx_resources(jme);
+ if(rc) {
+ jeprintk(netdev->name,
+ "Allocating resources for TX error"
+ ", Device STOPPED!\n");
+ goto err_out_free_rx_resources;
+ }
+
+ jme_enable_rx_engine(jme);
+ jme_enable_tx_engine(jme);
+
+ netif_start_queue(netdev);
+
+ if(jme->flags & JME_FLAG_POLL)
+ jme_interrupt_mode(jme);
+
+ jme_start_pcc_timer(jme);
+ }
+
+ goto out;
+
+err_out_free_rx_resources:
+ jme_free_rx_resources(jme);
+out:
+ atomic_inc(&jme->link_changing);
+}
+
+static void
+jme_rx_clean_tasklet(unsigned long arg)
+{
+ struct jme_adapter *jme = (struct jme_adapter*)arg;
+ struct dynpcc_info *dpi = &(jme->dpi);
+
+ jme_process_receive(jme, jme->rx_ring_size);
+ ++(dpi->intr_cnt);
+
+}
+
+static int
+jme_poll(JME_NAPI_HOLDER(holder), JME_NAPI_WEIGHT(budget))
+{
+ struct jme_adapter *jme = jme_napi_priv(holder);
+ struct net_device *netdev = jme->dev;
+ int rest;
+
+ rest = jme_process_receive(jme, JME_NAPI_WEIGHT_VAL(budget));
+
+ while(atomic_read(&jme->rx_empty) > 0) {
+ atomic_dec(&jme->rx_empty);
+ ++(NET_STAT(jme).rx_dropped);
+ jme_restart_rx_engine(jme);
+ }
+ atomic_inc(&jme->rx_empty);
+
+ if(rest) {
+ JME_RX_COMPLETE(netdev, holder);
+ jme_interrupt_mode(jme);
+ }
+
+ JME_NAPI_WEIGHT_SET(budget, rest);
+ return JME_NAPI_WEIGHT_VAL(budget) - rest;
+}
+
+static void
+jme_rx_empty_tasklet(unsigned long arg)
+{
+ struct jme_adapter *jme = (struct jme_adapter*)arg;
+
+ if(unlikely(atomic_read(&jme->link_changing) != 1))
+ return;
+
+ if(unlikely(!netif_carrier_ok(jme->dev)))
+ return;
+
+ queue_dbg(jme->dev->name, "RX Queue Full!\n");
+
+ jme_rx_clean_tasklet(arg);
+
+ while(atomic_read(&jme->rx_empty) > 0) {
+ atomic_dec(&jme->rx_empty);
+ ++(NET_STAT(jme).rx_dropped);
+ jme_restart_rx_engine(jme);
+ }
+ atomic_inc(&jme->rx_empty);
+}
+
+static void
+jme_wake_queue_if_stopped(struct jme_adapter *jme)
+{
+ struct jme_ring *txring = jme->txring;
+
+ smp_wmb();
+ if(unlikely(netif_queue_stopped(jme->dev) &&
+ atomic_read(&txring->nr_free) >= (jme->tx_wake_threshold))) {
+
+ queue_dbg(jme->dev->name, "TX Queue Waked.\n");
+ netif_wake_queue(jme->dev);
+
+ }
+
+}
+
+static void
+jme_tx_clean_tasklet(unsigned long arg)
+{
+ struct jme_adapter *jme = (struct jme_adapter*)arg;
+ struct jme_ring *txring = &(jme->txring[0]);
+ volatile struct txdesc *txdesc = txring->desc;
+ struct jme_buffer_info *txbi = txring->bufinf, *ctxbi, *ttxbi;
+ int i, j, cnt = 0, max, err, mask;
+
+ if(unlikely(!atomic_dec_and_test(&jme->tx_cleaning)))
+ goto out;
+
+ if(unlikely(atomic_read(&jme->link_changing) != 1))
+ goto out;
+
+ if(unlikely(!netif_carrier_ok(jme->dev)))
+ goto out;
+
+ max = jme->tx_ring_size - atomic_read(&txring->nr_free);
+ mask = jme->tx_ring_mask;
+
+ tx_dbg(jme->dev->name, "Tx Tasklet: In\n");
+
+ for(i = atomic_read(&txring->next_to_clean) ; cnt < max ; ) {
+
+ ctxbi = txbi + i;
+
+ if(likely(ctxbi->skb &&
+ !(txdesc[i].descwb.flags & TXWBFLAG_OWN))) {
+
+ err = txdesc[i].descwb.flags & TXWBFLAG_ALLERR;
+
+ tx_dbg(jme->dev->name,
+ "Tx Tasklet: Clean %d+%d\n",
+ i, ctxbi->nr_desc);
+
+ for(j = 1 ; j < ctxbi->nr_desc ; ++j) {
+ ttxbi = txbi + ((i + j) & (mask));
+ txdesc[(i + j) & (mask)].dw[0] = 0;
+
+ pci_unmap_page(jme->pdev,
+ ttxbi->mapping,
+ ttxbi->len,
+ PCI_DMA_TODEVICE);
+
+ ttxbi->mapping = 0;
+ ttxbi->len = 0;
+ }
+
+ dev_kfree_skb(ctxbi->skb);
+
+ cnt += ctxbi->nr_desc;
+
+ if(unlikely(err))
+ ++(NET_STAT(jme).tx_carrier_errors);
+ else {
+ ++(NET_STAT(jme).tx_packets);
+ NET_STAT(jme).tx_bytes += ctxbi->len;
+ }
+
+ ctxbi->skb = NULL;
+ ctxbi->len = 0;
+ ctxbi->start_xmit = 0;
+ }
+ else {
+ if(!ctxbi->skb)
+ tx_dbg(jme->dev->name,
+ "Tx Tasklet:"
+ " Stopped due to no skb.\n");
+ else
+ tx_dbg(jme->dev->name,
+ "Tx Tasklet:"
+ "Stopped due to not done.\n");
+ break;
+ }
+
+ i = (i + ctxbi->nr_desc) & mask;
+
+ ctxbi->nr_desc = 0;
+ }
+
+ tx_dbg(jme->dev->name,
+ "Tx Tasklet: Stop %d Jiffies %lu\n",
+ i, jiffies);
+
+ atomic_set(&txring->next_to_clean, i);
+ atomic_add(cnt, &txring->nr_free);
+
+ jme_wake_queue_if_stopped(jme);
+
+out:
+ atomic_inc(&jme->tx_cleaning);
+}
+
+static void
+jme_intr_msi(struct jme_adapter *jme, __u32 intrstat)
+{
+ /*
+ * Disable interrupt
+ */
+ jwrite32f(jme, JME_IENC, INTR_ENABLE);
+
+ if(intrstat & (INTR_LINKCH | INTR_SWINTR)) {
+ tasklet_schedule(&jme->linkch_task);
+ goto out_reenable;
+ }
+
+ if(intrstat & INTR_TMINTR)
+ tasklet_schedule(&jme->pcc_task);
+
+ if(intrstat & (INTR_PCCTXTO | INTR_PCCTX))
+ tasklet_schedule(&jme->txclean_task);
+
+ if(jme->flags & JME_FLAG_POLL) {
+ if(intrstat & INTR_RX0EMP)
+ atomic_inc(&jme->rx_empty);
+
+ if((intrstat & (INTR_PCCRX0TO | INTR_PCCRX0 | INTR_RX0EMP))) {
+ if(likely(JME_RX_SCHEDULE_PREP(jme))) {
+ jme_polling_mode(jme);
+ JME_RX_SCHEDULE(jme);
+ }
+ }
+ }
+ else {
+ if(intrstat & INTR_RX0EMP) {
+ atomic_inc(&jme->rx_empty);
+ tasklet_schedule(&jme->rxempty_task);
+ }
+ else if(intrstat & (INTR_PCCRX0TO | INTR_PCCRX0))
+ tasklet_schedule(&jme->rxclean_task);
+ }
+
+out_reenable:
+ /*
+ * Write 1 clear interrupt status
+ */
+ jwrite32f(jme, JME_IEVE, intrstat);
+
+ /*
+ * Re-enable interrupt
+ */
+ jwrite32f(jme, JME_IENS, INTR_ENABLE);
+
+
+}
+
+static irqreturn_t
+jme_intr(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct jme_adapter *jme = netdev_priv(netdev);
+ __u32 intrstat;
+
+ intrstat = jread32(jme, JME_IEVE);
+
+ /*
+ * Check if it's really an interrupt for us
+ */
+ if(unlikely(intrstat == 0))
+ return IRQ_NONE;
+
+ /*
+ * Check if the device still exist
+ */
+ if(unlikely(intrstat == ~((typeof(intrstat))0)))
+ return IRQ_NONE;
+
+ jme_intr_msi(jme, intrstat);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+jme_msi(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct jme_adapter *jme = netdev_priv(netdev);
+ __u32 intrstat;
+
+ pci_dma_sync_single_for_cpu(jme->pdev,
+ jme->shadow_dma,
+ sizeof(__u32) * SHADOW_REG_NR,
+ PCI_DMA_FROMDEVICE);
+ intrstat = jme->shadow_regs[SHADOW_IEVE];
+ jme->shadow_regs[SHADOW_IEVE] = 0;
+
+ jme_intr_msi(jme, intrstat);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+jme_msix_misc(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct jme_adapter *jme = netdev_priv(netdev);
+ __u32 intrstat;
+
+ pci_dma_sync_single_for_cpu(jme->pdev,
+ jme->shadow_dma,
+ sizeof(__u32) * SHADOW_REG_NR,
+ PCI_DMA_FROMDEVICE);
+ intrstat = jme->shadow_regs[SHADOW_IEVE];
+ jme->shadow_regs[SHADOW_IEVE] &= ~INTR_EN_MISC;
+
+ /*
+ * Disable interrupt
+ */
+ jwrite32f(jme, JME_IENC, INTR_EN_MISC);
+
+ if(intrstat & (INTR_LINKCH | INTR_SWINTR)) {
+ tasklet_schedule(&jme->linkch_task);
+ goto out_reenable;
+ }
+
+ if(intrstat & INTR_TMINTR)
+ tasklet_schedule(&jme->pcc_task);
+
+out_reenable:
+ /*
+ * Write 1 clear interrupt status
+ */
+ jwrite32f(jme, JME_IEVE, INTR_EN_MISC);
+
+ /*
+ * Re-enable interrupt
+ */
+ jwrite32f(jme, JME_IENS, INTR_EN_MISC);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+jme_msix_tx(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct jme_adapter *jme = netdev_priv(netdev);
+
+ /*
+ * Disable interrupt
+ */
+ jwrite32f(jme, JME_IENC, INTR_EN_TX);
+
+ if(unlikely(atomic_read(&jme->link_changing) != 1))
+ goto out_reenable;
+
+ tasklet_schedule(&jme->txclean_task);
+
+out_reenable:
+ /*
+ * Write 1 clear interrupt status
+ */
+ jwrite32f(jme, JME_IEVE, INTR_EN_TX | INTR_TX0);
+
+ /*
+ * Re-enable interrupt
+ */
+ jwrite32f(jme, JME_IENS, INTR_EN_TX);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+jme_msix_rx(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct jme_adapter *jme = netdev_priv(netdev);
+ __u32 intrstat;
+
+ pci_dma_sync_single_for_cpu(jme->pdev,
+ jme->shadow_dma,
+ sizeof(__u32) * SHADOW_REG_NR,
+ PCI_DMA_FROMDEVICE);
+ intrstat = jme->shadow_regs[SHADOW_IEVE];
+ jme->shadow_regs[SHADOW_IEVE] &= ~INTR_EN_RX0;
+
+ /*
+ * Disable interrupt
+ */
+ jwrite32f(jme, JME_IENC, INTR_EN_RX0);
+
+ if(unlikely(atomic_read(&jme->link_changing) != 1))
+ goto out_reenable;
+
+ if(jme->flags & JME_FLAG_POLL) {
+ if(intrstat & INTR_RX0EMP)
+ atomic_inc(&jme->rx_empty);
+
+ if(likely(JME_RX_SCHEDULE_PREP(jme))) {
+ jme_polling_mode(jme);
+ JME_RX_SCHEDULE(jme);
+ }
+ }
+ else {
+ if(intrstat & INTR_RX0EMP) {
+ atomic_inc(&jme->rx_empty);
+ tasklet_schedule(&jme->rxempty_task);
+ }
+ else if(intrstat & (INTR_PCCRX0TO | INTR_PCCRX0))
+ tasklet_schedule(&jme->rxclean_task);
+ }
+
+out_reenable:
+ /*
+ * Write 1 clear interrupt status
+ */
+ jwrite32f(jme, JME_IEVE, INTR_EN_RX0 | INTR_RX0);
+
+ /*
+ * Re-enable interrupt
+ */
+ jwrite32f(jme, JME_IENS, INTR_EN_RX0);
+
+ return IRQ_HANDLED;
+}
+
+static void
+jme_reset_link(struct jme_adapter *jme)
+{
+ jwrite32(jme, JME_TMCSR, TMCSR_SWIT);
+}
+
+static void
+jme_restart_an(struct jme_adapter *jme)
+{
+ __u32 bmcr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&jme->phy_lock, flags);
+ bmcr = jme_mdio_read(jme->dev, jme->mii_if.phy_id, MII_BMCR);
+ bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ jme_mdio_write(jme->dev, jme->mii_if.phy_id, MII_BMCR, bmcr);
+ spin_unlock_irqrestore(&jme->phy_lock, flags);
+}
+
+static void
+jme_setup_msix_info(struct jme_adapter *jme, struct msix_entry *msix_ent)
+{
+ int i;
+
+ for (i = 0; i < JME_MSIX_VEC_NR; i++) {
+ jme->msix[i].requested = false;
+ jme->msix[i].vector = msix_ent[i].vector;
+ strcpy(jme->msix[i].name, jme->dev->name);
+ }
+
+ jme->msix[0].handler = jme_msix_misc;
+ jme->msix[1].handler = jme_msix_tx;
+ jme->msix[2].handler = jme_msix_rx;
+
+ strcat(jme->msix[0].name, "-misc");
+ strcat(jme->msix[1].name, "-tx");
+ strcat(jme->msix[2].name, "-rx");
+}
+
+static void
+jme_fill_msix_regs(struct jme_adapter *jme)
+{
+ __u32 mask = 1, reg_msix = 0;
+ int i, vec;
+
+ for(i = 0 ; i < 32 ; ++i) {
+ if(mask & INTR_EN_TX)
+ vec = 1;
+ else if(mask & INTR_EN_RX0)
+ vec = 2;
+ else
+ vec = 0;
+
+ if(!(i & 7))
+ reg_msix = 0;
+ reg_msix |= (vec & 7) << ((i & 7) << 2);
+ if((i & 7) == 7)
+ jwrite32(jme,
+ JME_MSIX_ENT + ((i >> 3) << 2),
+ reg_msix);
+
+ mask <<= 1;
+ }
+}
+
+static int
+jme_request_msix_irq(struct jme_adapter *jme)
+{
+ int i, rc;
+ struct jme_msix_info *msix_info;
+
+ for (i = 0; i < JME_MSIX_VEC_NR; i++) {
+ msix_info = jme->msix + i;
+ rc = request_irq(msix_info->vector,
+ msix_info->handler,
+ 0,
+ msix_info->name,
+ jme->dev);
+ if(rc)
+ break;
+#if 0
+#ifdef CONFIG_SMP