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