irqreturn_t islpci_interrupt(int irq, void *config) { u32 reg; islpci_private *priv = config; struct net_device *ndev = priv->ndev; void __iomem *device = priv->device_base; int powerstate = ISL38XX_PSM_POWERSAVE_STATE; /* lock the interrupt handler */ spin_lock(&priv->slock); /* received an interrupt request on a shared IRQ line * first check whether the device is in sleep mode */ reg = readl(device + ISL38XX_CTRL_STAT_REG); if (reg & ISL38XX_CTRL_STAT_SLEEPMODE) /* device is in sleep mode, IRQ was generated by someone else */ { #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n"); #endif spin_unlock(&priv->slock); return IRQ_NONE; } /* check whether there is any source of interrupt on the device */ reg = readl(device + ISL38XX_INT_IDENT_REG); /* also check the contents of the Interrupt Enable Register, because this * will filter out interrupt sources from other devices on the same irq ! */ reg &= readl(device + ISL38XX_INT_EN_REG); reg &= ISL38XX_INT_SOURCES; if (reg != 0) { if (islpci_get_state(priv) != PRV_STATE_SLEEP) powerstate = ISL38XX_PSM_ACTIVE_STATE; /* reset the request bits in the Identification register */ isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG); #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_FUNCTION_CALLS, "IRQ: Identification register 0x%p 0x%x \n", device, reg); #endif /* check for each bit in the register separately */ if (reg & ISL38XX_INT_IDENT_UPDATE) { #if VERBOSE > SHOW_ERROR_MESSAGES /* Queue has been updated */ DEBUG(SHOW_TRACING, "IRQ: Update flag \n"); DEBUG(SHOW_QUEUE_INDEXES, "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n", le32_to_cpu(priv->control_block-> driver_curr_frag[0]), le32_to_cpu(priv->control_block-> driver_curr_frag[1]), le32_to_cpu(priv->control_block-> driver_curr_frag[2]), le32_to_cpu(priv->control_block-> driver_curr_frag[3]), le32_to_cpu(priv->control_block-> driver_curr_frag[4]), le32_to_cpu(priv->control_block-> driver_curr_frag[5]) ); DEBUG(SHOW_QUEUE_INDEXES, "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n", le32_to_cpu(priv->control_block-> device_curr_frag[0]), le32_to_cpu(priv->control_block-> device_curr_frag[1]), le32_to_cpu(priv->control_block-> device_curr_frag[2]), le32_to_cpu(priv->control_block-> device_curr_frag[3]), le32_to_cpu(priv->control_block-> device_curr_frag[4]), le32_to_cpu(priv->control_block-> device_curr_frag[5]) ); #endif /* cleanup the data low transmit queue */ islpci_eth_cleanup_transmit(priv, priv->control_block); /* device is in active state, update the * powerstate flag if necessary */ powerstate = ISL38XX_PSM_ACTIVE_STATE; /* check all three queues in priority order * call the PIMFOR receive function until the * queue is empty */ if (isl38xx_in_queue(priv->control_block, ISL38XX_CB_RX_MGMTQ) != 0) { #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Received frame in Management Queue\n"); #endif islpci_mgt_receive(ndev); islpci_mgt_cleanup_transmit(ndev); /* Refill slots in receive queue */ islpci_mgmt_rx_fill(ndev); /* no need to trigger the device, next islpci_mgt_transaction does it */ } while (isl38xx_in_queue(priv->control_block, ISL38XX_CB_RX_DATA_LQ) != 0) { #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Received frame in Data Low Queue \n"); #endif islpci_eth_receive(priv); } /* check whether the data transmit queues were full */ if (priv->data_low_tx_full) { /* check whether the transmit is not full anymore */ if (ISL38XX_CB_TX_QSIZE - isl38xx_in_queue(priv->control_block, ISL38XX_CB_TX_DATA_LQ) >= ISL38XX_MIN_QTHRESHOLD) { /* nope, the driver is ready for more network frames */ netif_wake_queue(priv->ndev); /* reset the full flag */ priv->data_low_tx_full = 0; } } } if (reg & ISL38XX_INT_IDENT_INIT) { /* Device has been initialized */ #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Init flag, device initialized \n"); #endif wake_up(&priv->reset_done); } if (reg & ISL38XX_INT_IDENT_SLEEP) { /* Device intends to move to powersave state */ #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Sleep flag \n"); #endif isl38xx_handle_sleep_request(priv->control_block, &powerstate, priv->device_base); } if (reg & ISL38XX_INT_IDENT_WAKEUP) { /* Device has been woken up to active state */ #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Wakeup flag \n"); #endif isl38xx_handle_wakeup(priv->control_block, &powerstate, priv->device_base); } } else { #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n"); #endif spin_unlock(&priv->slock); return IRQ_NONE; } /* sleep -> ready */ if (islpci_get_state(priv) == PRV_STATE_SLEEP && powerstate == ISL38XX_PSM_ACTIVE_STATE) islpci_set_state(priv, PRV_STATE_READY); /* !sleep -> sleep */ if (islpci_get_state(priv) != PRV_STATE_SLEEP && powerstate == ISL38XX_PSM_POWERSAVE_STATE) islpci_set_state(priv, PRV_STATE_SLEEP); /* unlock the interrupt handler */ spin_unlock(&priv->slock); return IRQ_HANDLED; }
/****************************************************************************** Device Interrupt Handler ******************************************************************************/ void islpci_interrupt(int irq, void *config, struct pt_regs *regs) { u32 reg; islpci_private *private_config = config; void *device = private_config->device_id; u16 status; // received an interrupt request on a shared IRQ line // first check whether the device is in sleep mode if( reg = readl( device + ISL38XX_CTRL_STAT_REG), reg & ISL38XX_CTRL_STAT_SLEEPMODE ) // device is in sleep mode, IRQ was generated by someone else return; // lock the interrupt handler spin_lock( &private_config->slock ); #ifdef CONFIG_ARCH_ISL3893 // check the interrupt source on the host registers reg = (u32) uPCI->ARMIntReg; reg &= (u32) uPCI->ARMIntEnReg; // pci_unmap_single( private_config->pci_device, private_config->control_block, // sizeof(struct isl38xx_cb), PCI_DMA_FROMDEVICE ); flush_cache_all(); #else // check whether there is any source of interrupt on the device reg = readl(device + ISL38XX_INT_IDENT_REG); // also check the contents of the Interrupt Enable Register, because this // will filter out interrupt sources from other devices on the same irq ! reg &= readl(device + ISL38XX_INT_EN_REG); #endif if (reg &= ISL38XX_INT_SOURCES, reg != 0) { #ifdef CONFIG_ARCH_ISL3893 // write the acknowledge register on the host uPCI->ARMIntAckReg = reg; #else // reset the request bits in the Identification register writel(reg, device + ISL38XX_INT_ACK_REG); #endif #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_FUNCTION_CALLS, "IRQ: Identification register value 0x%x \n", reg ); #endif // check for each bit in the register separately if (reg & ISL38XX_INT_IDENT_UPDATE) { #if VERBOSE > SHOW_ERROR_MESSAGES // Queue has been updated DEBUG(SHOW_TRACING, "IRQ: Update flag \n"); DEBUG(SHOW_QUEUE_INDEXES, "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n", le32_to_cpu(private_config->control_block->driver_curr_frag[0]), le32_to_cpu(private_config->control_block->driver_curr_frag[1]), le32_to_cpu(private_config->control_block->driver_curr_frag[2]), le32_to_cpu(private_config->control_block->driver_curr_frag[3]), le32_to_cpu(private_config->control_block->driver_curr_frag[4]), le32_to_cpu(private_config->control_block->driver_curr_frag[5]) ); DEBUG(SHOW_QUEUE_INDEXES, "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n", le32_to_cpu(private_config->control_block->device_curr_frag[0]), le32_to_cpu(private_config->control_block->device_curr_frag[1]), le32_to_cpu(private_config->control_block->device_curr_frag[2]), le32_to_cpu(private_config->control_block->device_curr_frag[3]), le32_to_cpu(private_config->control_block->device_curr_frag[4]), le32_to_cpu(private_config->control_block->device_curr_frag[5]) ); #endif // device is in active state, update the powerstate flag private_config->powerstate = ISL38XX_PSM_ACTIVE_STATE; // check all three queues in priority order // call the PIMFOR receive function until the queue is empty while (isl38xx_in_queue(private_config->control_block, ISL38XX_CB_RX_MGMTQ) != 0) { #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Received frame in Management Queue\n"); #endif islpci_mgt_receive(private_config); // call the pimfor transmit function for processing the next // management frame in the shadow queue islpci_mgt_transmit( private_config ); } while (isl38xx_in_queue(private_config->control_block, ISL38XX_CB_RX_DATA_LQ) != 0) { #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "Received frame in Data Low Queue \n"); #endif islpci_eth_receive(private_config); } // check whether the data transmit queues were full if (private_config->data_low_tx_full) { // check whether the transmit is not full anymore if( ISL38XX_CB_TX_QSIZE - isl38xx_in_queue( private_config->control_block, ISL38XX_CB_TX_DATA_LQ ) >= ISL38XX_MIN_QTHRESHOLD ) { // nope, the driver is ready for more network frames netif_wake_queue(private_config->my_module); // reset the full flag private_config->data_low_tx_full = 0; } } } if (reg & ISL38XX_INT_IDENT_INIT) { // Device has been initialized #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "IRQ: Init flag, device initialized \n"); #endif // perform card initlialization by sending objects islpci_mgt_initialize( private_config ); } if (reg & ISL38XX_INT_IDENT_SLEEP) { // Device intends to move to powersave state #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_ANYTHING, "IRQ: Sleep flag \n"); #endif isl38xx_handle_sleep_request( private_config->control_block, &private_config->powerstate, private_config->remapped_device_base ); } if (reg & ISL38XX_INT_IDENT_WAKEUP) { // Device has been woken up to active state #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_ANYTHING, "IRQ: Wakeup flag \n"); #endif isl38xx_handle_wakeup( private_config->control_block, &private_config->powerstate, private_config->remapped_device_base ); } } // unlock the interrupt handler spin_unlock( &private_config->slock ); return; }