1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
14 */
15
16 #include "cpqary3.h"
17
18 /*
19 * Function : cpqary3_hw_isr
20 * Description : This routine determines if this instance of the
21 * HBA interrupted and if positive triggers a software
22 * interrupt.
23 * For SAS controllers which operate in performant mode
24 * we clear the interrupt.
25 * For CISS controllers which operate in simple mode
26 * we get the tag value.
27 * Called By : kernel
28 * Parameters : per-controller
29 * Calls : cpqary3_check_ctlr_intr()
30 * Return Values: DDI_INTR_CLAIMED/UNCLAIMED
31 * [We either CLAIM the interrupt or Discard it]
32 */
33 uint_t
cpqary3_hw_isr(caddr_t per_ctlr)34 cpqary3_hw_isr(caddr_t per_ctlr)
35 {
36 uint8_t need_swintr;
37 cpqary3_t *cpqary3p;
38 cpqary3_drvr_replyq_t *replyq_ptr;
39 volatile CfgTable_t *ctp;
40 uint32_t spr0;
41 uint32_t doorbell_status;
42 uint32_t tag;
43
44 cpqary3p = (void *)per_ctlr;
45 ctp = (CfgTable_t *)cpqary3p->ct;
46 replyq_ptr = (cpqary3_drvr_replyq_t *)cpqary3p->drvr_replyq;
47
48 if (CPQARY3_FAILURE == cpqary3p->check_ctlr_intr(cpqary3p)) {
49 if (cpqary3p->heartbeat ==
50 DDI_GET32(cpqary3p, &ctp->HeartBeat)) {
51 if (0x2 & ddi_get32(cpqary3p->odr_handle,
52 (uint32_t *)cpqary3p->odr)) {
53 spr0 = ddi_get32(cpqary3p->spr0_handle,
54 (uint32_t *)cpqary3p->spr0);
55 spr0 = spr0 >> 16;
56 cmn_err(CE_WARN, "CPQary3 : %s HBA firmware "
57 "Locked !!! Lockup Code: 0x%x",
58 cpqary3p->hba_name, spr0);
59 cmn_err(CE_WARN, "CPQary3 : Please reboot "
60 "the system");
61 ddi_put32(cpqary3p->odr_cl_handle,
62 (uint32_t *)cpqary3p->odr_cl, 0x2);
63 cpqary3_intr_onoff(cpqary3p,
64 CPQARY3_INTR_DISABLE);
65 if (cpqary3p->host_support & 0x4) {
66 cpqary3_lockup_intr_onoff(cpqary3p,
67 CPQARY3_LOCKUP_INTR_DISABLE);
68 }
69 cpqary3p->controller_lockup = CPQARY3_TRUE;
70 }
71 return (DDI_INTR_CLAIMED);
72 }
73 return (DDI_INTR_UNCLAIMED);
74 }
75
76 /* PERF */
77
78 /*
79 * We decided that we will have only one retrieve function for
80 * both simple and performant mode. To achieve this we have to mimic
81 * what controller does for performant mode in simple mode.
82 * For simple mode we are making replq_simple_ptr and
83 * replq_headptr of performant
84 * mode point to the same location in the reply queue.
85 * For the performant mode, we clear the interrupt
86 */
87
88 if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) {
89 while ((tag = ddi_get32(cpqary3p->opq_handle,
90 (uint32_t *)cpqary3p->opq)) != 0xFFFFFFFF) {
91 replyq_ptr->replyq_simple_ptr[0] = tag;
92 replyq_ptr->replyq_simple_ptr[0] |=
93 replyq_ptr->simple_cyclic_indicator;
94 ++replyq_ptr->simple_index;
95
96 if (replyq_ptr->simple_index == replyq_ptr->max_index) {
97 replyq_ptr->simple_index = 0;
98 /* Toggle at wraparound */
99 replyq_ptr->simple_cyclic_indicator =
100 (replyq_ptr->simple_cyclic_indicator == 0) ?
101 1 : 0;
102 replyq_ptr->replyq_simple_ptr =
103 /* LINTED: alignment */
104 (uint32_t *)(replyq_ptr->replyq_start_addr);
105 } else {
106 replyq_ptr->replyq_simple_ptr += 2;
107 }
108 }
109 } else {
110 doorbell_status = ddi_get32(cpqary3p->odr_handle,
111 (uint32_t *)cpqary3p->odr);
112 if (doorbell_status & 0x1) {
113 ddi_put32(cpqary3p->odr_cl_handle,
114 (uint32_t *)cpqary3p->odr_cl,
115 (ddi_get32(cpqary3p->odr_cl_handle,
116 (uint32_t *)cpqary3p->odr_cl) | 0x1));
117 doorbell_status = ddi_get32(cpqary3p->odr_handle,
118 (uint32_t *)cpqary3p->odr);
119 }
120 }
121
122 /* PERF */
123
124 /*
125 * If s/w interrupt handler is already running, do not trigger another
126 * since packets have already been transferred to Retrieved Q.
127 * Else, Set swintr_flag to state to the s/w interrupt handler
128 * that it has a job to do.
129 * trigger the s/w interrupt handler
130 * Claim the interrupt
131 */
132
133 mutex_enter(&cpqary3p->hw_mutex);
134
135 if (cpqary3p->swintr_flag == CPQARY3_TRUE) {
136 need_swintr = CPQARY3_FALSE;
137 } else {
138 need_swintr = CPQARY3_TRUE;
139 cpqary3p->swintr_flag = CPQARY3_TRUE;
140 }
141
142 mutex_exit(&cpqary3p->hw_mutex);
143
144 if (CPQARY3_TRUE == need_swintr)
145 ddi_trigger_softintr(cpqary3p->cpqary3_softintr_id);
146
147 return (DDI_INTR_CLAIMED);
148 }
149
150 /*
151 * Function : cpqary3_sw_isr
152 * Description : This routine determines if this instance of the
153 * software interrupt handler was triggered by its
154 * respective h/w interrupt handler and if affermative
155 * processes the completed commands.
156 * Called By : kernel (Triggered by : cpqary3_hw_isr)
157 * Parameters : per-controller
158 * Calls : cpqary3_retrieve()
159 * Return Values: DDI_INTR_CLAIMED/UNCLAIMED
160 * [We either CLAIM the interrupr or DON'T]
161 */
162 uint_t
cpqary3_sw_isr(caddr_t per_ctlr)163 cpqary3_sw_isr(caddr_t per_ctlr)
164 {
165 cpqary3_t *cpqary3p;
166
167 cpqary3p = (void *)per_ctlr;
168 if (!cpqary3p) {
169 cmn_err(CE_PANIC, "CPQary3 : Software Interrupt Service "
170 "Routine invoked with NULL pointer argument \n");
171 }
172
173 /*
174 * Ensure that our hardware routine actually triggered this routine.
175 * If it was not the case, do NOT CLAIM the interrupt
176 */
177
178 mutex_enter(&cpqary3p->hw_mutex);
179 if (CPQARY3_TRUE != cpqary3p->swintr_flag) {
180 mutex_exit(&cpqary3p->hw_mutex);
181 return (DDI_INTR_UNCLAIMED);
182 }
183
184 cpqary3p->swintr_flag = CPQARY3_FALSE;
185
186 /* PERF */
187 mutex_exit(&cpqary3p->hw_mutex);
188 (void) cpqary3_retrieve(cpqary3p);
189 /* PERF */
190
191 return (DDI_INTR_CLAIMED);
192 }
193