root/usr/src/uts/common/io/cpqary3/cpqary3_isr.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
 */

#include "cpqary3.h"

/*
 * Function     :       cpqary3_hw_isr
 * Description  :       This routine determines if this instance of the
 *                      HBA interrupted and if positive triggers a software
 *                      interrupt.
 *                      For SAS controllers which operate in performant mode
 *                      we clear the interrupt.
 *                      For CISS controllers which operate in simple mode
 *                      we get the tag value.
 * Called By    :       kernel
 * Parameters   :       per-controller
 * Calls        :       cpqary3_check_ctlr_intr()
 * Return Values:       DDI_INTR_CLAIMED/UNCLAIMED
 *                      [We either CLAIM the interrupt or Discard it]
 */
uint_t
cpqary3_hw_isr(caddr_t per_ctlr)
{
        uint8_t                 need_swintr;
        cpqary3_t               *cpqary3p;
        cpqary3_drvr_replyq_t   *replyq_ptr;
        volatile CfgTable_t     *ctp;
        uint32_t                spr0;
        uint32_t                doorbell_status;
        uint32_t                tag;

        cpqary3p = (void *)per_ctlr;
        ctp = (CfgTable_t *)cpqary3p->ct;
        replyq_ptr = (cpqary3_drvr_replyq_t *)cpqary3p->drvr_replyq;

        if (CPQARY3_FAILURE == cpqary3p->check_ctlr_intr(cpqary3p)) {
                if (cpqary3p->heartbeat ==
                    DDI_GET32(cpqary3p, &ctp->HeartBeat)) {
                        if (0x2 & ddi_get32(cpqary3p->odr_handle,
                            (uint32_t *)cpqary3p->odr)) {
                                spr0 = ddi_get32(cpqary3p->spr0_handle,
                                    (uint32_t *)cpqary3p->spr0);
                                spr0 = spr0 >> 16;
                                cmn_err(CE_WARN, "CPQary3 : %s HBA firmware "
                                    "Locked !!!  Lockup Code: 0x%x",
                                    cpqary3p->hba_name, spr0);
                                cmn_err(CE_WARN, "CPQary3 : Please reboot "
                                    "the system");
                                ddi_put32(cpqary3p->odr_cl_handle,
                                    (uint32_t *)cpqary3p->odr_cl, 0x2);
                                cpqary3_intr_onoff(cpqary3p,
                                    CPQARY3_INTR_DISABLE);
                                if (cpqary3p->host_support & 0x4) {
                                        cpqary3_lockup_intr_onoff(cpqary3p,
                                            CPQARY3_LOCKUP_INTR_DISABLE);
                                }
                                cpqary3p->controller_lockup = CPQARY3_TRUE;
                        }
                        return (DDI_INTR_CLAIMED);
                }
                return (DDI_INTR_UNCLAIMED);
        }

        /* PERF */

        /*
         * We decided that we will have only one retrieve function for
         * both simple and performant mode. To achieve this we have to mimic
         * what controller does for performant mode in simple mode.
         * For simple mode we are making replq_simple_ptr and
         * replq_headptr of performant
         * mode point to the same location in the reply queue.
         * For the performant mode, we clear the interrupt
         */

        if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) {
                while ((tag = ddi_get32(cpqary3p->opq_handle,
                    (uint32_t *)cpqary3p->opq)) != 0xFFFFFFFF) {
                        replyq_ptr->replyq_simple_ptr[0] = tag;
                        replyq_ptr->replyq_simple_ptr[0] |=
                            replyq_ptr->simple_cyclic_indicator;
                        ++replyq_ptr->simple_index;

                        if (replyq_ptr->simple_index == replyq_ptr->max_index) {
                                replyq_ptr->simple_index = 0;
                                /* Toggle at wraparound */
                                replyq_ptr->simple_cyclic_indicator =
                                    (replyq_ptr->simple_cyclic_indicator == 0) ?
                                    1 : 0;
                                replyq_ptr->replyq_simple_ptr =
                                    /* LINTED: alignment */
                                    (uint32_t *)(replyq_ptr->replyq_start_addr);
                        } else {
                                replyq_ptr->replyq_simple_ptr += 2;
                        }
                }
        } else {
                doorbell_status = ddi_get32(cpqary3p->odr_handle,
                    (uint32_t *)cpqary3p->odr);
                if (doorbell_status & 0x1) {
                        ddi_put32(cpqary3p->odr_cl_handle,
                            (uint32_t *)cpqary3p->odr_cl,
                            (ddi_get32(cpqary3p->odr_cl_handle,
                            (uint32_t *)cpqary3p->odr_cl) | 0x1));
                        doorbell_status = ddi_get32(cpqary3p->odr_handle,
                            (uint32_t *)cpqary3p->odr);
                }
        }

        /* PERF */

        /*
         * If s/w interrupt handler is already running, do not trigger another
         * since packets have already been transferred to Retrieved Q.
         * Else, Set swintr_flag to state to the s/w interrupt handler
         * that it has a job to do.
         * trigger the s/w interrupt handler
         * Claim the interrupt
         */

        mutex_enter(&cpqary3p->hw_mutex);

        if (cpqary3p->swintr_flag == CPQARY3_TRUE) {
                need_swintr = CPQARY3_FALSE;
        } else {
                need_swintr = CPQARY3_TRUE;
                cpqary3p->swintr_flag = CPQARY3_TRUE;
        }

        mutex_exit(&cpqary3p->hw_mutex);

        if (CPQARY3_TRUE == need_swintr)
                ddi_trigger_softintr(cpqary3p->cpqary3_softintr_id);

        return (DDI_INTR_CLAIMED);
}

/*
 * Function     :       cpqary3_sw_isr
 * Description  :       This routine determines if this instance of the
 *                      software interrupt handler was triggered by its
 *                      respective h/w interrupt handler and if affermative
 *                      processes the completed commands.
 * Called By    :       kernel (Triggered by : cpqary3_hw_isr)
 * Parameters   :       per-controller
 * Calls        :       cpqary3_retrieve()
 * Return Values:       DDI_INTR_CLAIMED/UNCLAIMED
 *                      [We either CLAIM the interrupr or DON'T]
 */
uint_t
cpqary3_sw_isr(caddr_t per_ctlr)
{
        cpqary3_t       *cpqary3p;

        cpqary3p = (void *)per_ctlr;
        if (!cpqary3p) {
                cmn_err(CE_PANIC, "CPQary3 : Software Interrupt Service "
                    "Routine invoked with NULL pointer argument \n");
        }

        /*
         * Ensure that our hardware routine actually triggered this routine.
         * If it was not the case, do NOT CLAIM the interrupt
         */

        mutex_enter(&cpqary3p->hw_mutex);
        if (CPQARY3_TRUE != cpqary3p->swintr_flag) {
                mutex_exit(&cpqary3p->hw_mutex);
                return (DDI_INTR_UNCLAIMED);
        }

        cpqary3p->swintr_flag = CPQARY3_FALSE;

        /* PERF */
        mutex_exit(&cpqary3p->hw_mutex);
        (void) cpqary3_retrieve(cpqary3p);
        /* PERF */

        return (DDI_INTR_CLAIMED);
}