Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

unsigned long sah_Handle_Interrupt ( sah_Execute_Status  hw_status  ) 

The bulk of the interrupt handling code.

the dynameic power management flag is false when power management is not asserted and true when dpm is.

This functionx processes the status register of the Sahara, updates the state of the finished queue entry, and then tries to find more work for Sahara to do.

Parameters:
hw_status The status register of Sahara at time of interrupt. The Clear interrupt bit is already handled by this register read prior to entry into this function.
Returns:
void

Definition at line 88 of file sah_status_manager.c.

References sah_Head_Desc::desc, desc_queue_lock, done1_count, done1busy2_count, done1done2_count, sah_Head_Desc::error_status, sah_Head_Desc::fault_address, sah_Head_Desc::op_status, os_lock_save_context, os_unlock_restore_context, SAH_EXEC_BUSY, SAH_EXEC_DONE1, SAH_EXEC_DONE1_BIT, SAH_EXEC_DONE1_BUSY2, SAH_EXEC_DONE1_DONE2, SAH_EXEC_DONE1_ERROR2, SAH_EXEC_ERROR1, SAH_EXEC_FAULT, SAH_EXEC_IDLE, SAH_EXEC_STATE_MASK, sah_Find_With_State(), sah_HW_Read_DAR(), sah_HW_Read_Error_Status(), sah_HW_Read_Fault_Address(), sah_HW_Read_IDAR(), sah_HW_Read_Op_Status(), sah_HW_Write_DAR(), SAH_STATE_OFF_SAHARA, SAH_STATE_ON_SAHARA, SAH_STATE_PENDING, sah_Head_Desc::status, and STATUS_ERROR.

Referenced by sah_Intr_Top_Half(), and sahara_timeout_handler().

{
      unsigned long reset_flag = 0; /* assume no SAHARA reset needed */
      os_lock_context_t lock_flags;
      sah_Head_Desc *current_entry;

      /* HW status at time of interrupt */
      sah_Execute_Status state = hw_status & SAH_EXEC_STATE_MASK;

      do {
            uint32_t dar;

#ifdef DIAG_INT_COUNT
            if (state == SAH_EXEC_DONE1) {
                  done1_count++;
            } else if (state == SAH_EXEC_DONE1_BUSY2) {
                  done1busy2_count++;
            } else if (state == SAH_EXEC_DONE1_DONE2) {
                  done1done2_count++;
            }
#endif

            /* if the first entry on sahara has completed... */
            if ((state & SAH_EXEC_DONE1_BIT) ||
                (state == SAH_EXEC_ERROR1)) {
                  /* lock queue while searching */
                  os_lock_save_context(desc_queue_lock, lock_flags);
                  current_entry =
                      sah_Find_With_State(SAH_STATE_ON_SAHARA);
                  os_unlock_restore_context(desc_queue_lock, lock_flags);

                  /* an active descriptor was not found */
                  if (current_entry == NULL) {
                        /* change state to avoid an infinite loop (possible if
                         * state is SAH_EXEC_DONE1_BUSY2 first time into loop) */
                        hw_status = SAH_EXEC_IDLE;
#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
                        LOG_KDIAG
                            ("Interrupt received with nothing on queue.");
#endif
                  } else {
                        /* SAHARA has completed its work on this descriptor chain */
                        current_entry->status = SAH_STATE_OFF_SAHARA;

                        if (state == SAH_EXEC_ERROR1) {
                           if (hw_status & STATUS_ERROR) {
                                /* Gather extra diagnostic information */
                                current_entry->fault_address =
                                  sah_HW_Read_Fault_Address();
                                /* Read this last - it clears the error */
                                current_entry->error_status =
                                  sah_HW_Read_Error_Status();
                                current_entry->op_status = 0;
#ifdef FSL_HAVE_SAHARA4
                           } else {
                                current_entry->op_status =
                                          sah_HW_Read_Op_Status();
                                current_entry->error_status = 0;
#endif
                           }

                        } else {
                              /* indicate that no errors were found with descriptor
                               * chain 1 */
                              current_entry->error_status = 0;
                              current_entry->op_status = 0;

                              /* is there a second, successfully, completed descriptor
                               * chain? (done1/error2 processing is handled later) */
                              if (state == SAH_EXEC_DONE1_DONE2) {
                                    os_lock_save_context
                                        (desc_queue_lock,
                                         lock_flags);
                                    current_entry =
                                        sah_Find_With_State
                                        (SAH_STATE_ON_SAHARA);
                                    os_unlock_restore_context
                                        (desc_queue_lock,
                                         lock_flags);

                                    if (current_entry == NULL) {
#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
                                          LOG_KDIAG
                                              ("Done1_Done2 Interrupt received with "
                                               "one entry on queue.");
#endif
                                    } else {
                                          /* indicate no errors in descriptor chain 2 */
                                          current_entry->
                                              error_status = 0;
                                          current_entry->status =
                                              SAH_STATE_OFF_SAHARA;
                                    }
                              }
                        }
                  }

#ifdef SAHARA_POWER_MANAGEMENT
                  /* check dynamic power management is not asserted */
                  if (!sah_dpm_flag) {
#endif
                        do {
                              /* protect DAR and main_queue */
                              os_lock_save_context(desc_queue_lock,
                                               lock_flags);
                              dar = sah_HW_Read_DAR();
                              /* check if SAHARA has space for another descriptor. SAHARA
                               * only accepts up to the DAR queue size number of DAR
                               * entries, after that 'dar' will not be zero until the
                               * pending interrupt is serviced */
                              if (dar == 0) {
                                    current_entry =
                                        sah_Find_With_State
                                        (SAH_STATE_PENDING);
                                    if (current_entry != NULL) {
#ifndef SUBMIT_MULTIPLE_DARS
                                          /* BUG FIX: state machine can transition from Done1
                                           * Busy2 directly to Idle. To fix that problem,
                                           * only one DAR is being allowed on SAHARA at a
                                           * time.  If a high level interrupt has happened,
                                           * there could * be an active descriptor chain */
                                          if (sah_Find_With_State
                                              (SAH_STATE_ON_SAHARA)
                                              == NULL) {
#endif
#if defined(DIAG_DRV_IF) && defined(DIAG_DURING_INTERRUPT)
                                                sah_Dump_Chain
                                                    (&current_entry->
                                                     desc,
                                                     current_entry->
                                                     desc.
                                                     dma_addr);
#endif                        /* DIAG_DRV_IF */
                                                sah_HW_Write_DAR
                                                    (current_entry->
                                                     desc.
                                                     dma_addr);
                                                current_entry->
                                                    status =
                                                    SAH_STATE_ON_SAHARA;
#ifndef SUBMIT_MULTIPLE_DARS
                                          }
                                          current_entry = NULL;   /* exit loop */
#endif
                                    }
                              }
                              os_unlock_restore_context
                                  (desc_queue_lock, lock_flags);
                        } while ((dar == 0) && (current_entry != NULL));
#ifdef SAHARA_POWER_MANAGEMENT
                  }     /* sah_device_power_manager */
#endif
            } else {
                  if (state == SAH_EXEC_FAULT) {
                        sah_Head_Desc *previous_entry;      /* point to chain 1 */
                        /* Address of request when fault occured */
                        uint32_t bad_dar = sah_HW_Read_IDAR();

                        reset_flag = 1;   /* SAHARA needs to be reset */

                        /* get first of possible two descriptor chain that was
                         * on SAHARA */
                        os_lock_save_context(desc_queue_lock,
                                         lock_flags);
                        previous_entry =
                            sah_Find_With_State(SAH_STATE_ON_SAHARA);
                        os_unlock_restore_context(desc_queue_lock,
                                            lock_flags);

                        /* if it exists, continue processing the fault */
                        if (previous_entry) {
                              /* assume this chain didn't complete correctly */
                              previous_entry->error_status = -1;
                              previous_entry->status =
                                  SAH_STATE_OFF_SAHARA;

                              /* get the second descriptor chain */
                              os_lock_save_context(desc_queue_lock,
                                               lock_flags);
                              current_entry =
                                  sah_Find_With_State
                                  (SAH_STATE_ON_SAHARA);
                              os_unlock_restore_context
                                  (desc_queue_lock, lock_flags);

                              /* if it exists, continue processing both chains */
                              if (current_entry) {
                                    /* assume this chain didn't complete correctly */
                                    current_entry->error_status =
                                        -1;
                                    current_entry->status =
                                        SAH_STATE_OFF_SAHARA;

                                    /* now see if either can be identified as the one
                                     * in progress when the fault occured */
                                    if (current_entry->desc.
                                        dma_addr == bad_dar) {
                                          /* the second descriptor chain was active when the
                                           * fault occured, so the first descriptor chain
                                           * was successfull */
                                          previous_entry->
                                              error_status = 0;
                                    } else {
                                          if (previous_entry->
                                              desc.dma_addr ==
                                              bad_dar) {
                                                /* if the first chain was in progress when the
                                                 * fault occured, the second has not yet been
                                                 * touched, so reset it to PENDING */
                                                current_entry->
                                                    status =
                                                    SAH_STATE_PENDING;
                                          }
                                    }
                              }
                        }
#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
                  } else {
                        /* shouldn't ever get here */
                        if (state == SAH_EXEC_BUSY) {
                              LOG_KDIAG
                                  ("Got Sahara interrupt in Busy state");
                        } else {
                              if (state == SAH_EXEC_IDLE) {
                                    LOG_KDIAG
                                        ("Got Sahara interrupt in Idle state");
                              } else {
                                    LOG_KDIAG
                                        ("Got Sahara interrupt in unknown state");
                              }
                        }
#endif
                  }
            }

            /* haven't handled the done1/error2 (the error 2 part), so setup to
             * do that now. Otherwise, exit loop */
            state = (state == SAH_EXEC_DONE1_ERROR2) ?
                SAH_EXEC_ERROR1 : SAH_EXEC_IDLE;

            /* Keep going while further status is available. */
      } while (state == SAH_EXEC_ERROR1);

    /* Disabling Sahara Clock only if the hardware is in idle state and
          the DAR queue is empty.*/
      os_lock_save_context(desc_queue_lock, lock_flags);
      current_entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
      os_unlock_restore_context(desc_queue_lock, lock_flags);

      if ((current_entry == NULL) && (state == SAH_EXEC_IDLE)) {

#ifdef DIAG_DRV_IF
            LOG_KDIAG("SAHARA : Disabling the clocks\n")
#endif                        /* DIAG_DRV_IF */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
            mxc_clks_disable(SAHARA2_CLK);
#else
            {
                  struct clk *clk = clk_get(NULL, "sahara_clk");
                  if (clk != ERR_PTR(ENOENT))
                        clk_disable(clk);
                  clk_put(clk);
            }
#endif

      }

      return reset_flag;
}


Generated by  Doxygen 1.6.0   Back to index