1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
/*
* 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);
}
|