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 2016 Joyent, Inc.
14 */
15
16 #include <sys/scsi/adapters/smrt/smrt.h>
17
18 uint_t
smrt_isr_hw_simple(caddr_t arg1,caddr_t arg2)19 smrt_isr_hw_simple(caddr_t arg1, caddr_t arg2)
20 {
21 _NOTE(ARGUNUSED(arg2))
22
23 /* LINTED: E_BAD_PTR_CAST_ALIGN */
24 smrt_t *smrt = (smrt_t *)arg1;
25 uint32_t isr = smrt_get32(smrt, CISS_I2O_INTERRUPT_STATUS);
26 hrtime_t now = gethrtime();
27
28 mutex_enter(&smrt->smrt_mutex);
29 if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)) {
30 smrt->smrt_stats.smrts_unclaimed_interrupts++;
31 smrt->smrt_last_interrupt_unclaimed = now;
32
33 /*
34 * We should not be receiving interrupts from the controller
35 * while the driver is not running.
36 */
37 mutex_exit(&smrt->smrt_mutex);
38 return (DDI_INTR_UNCLAIMED);
39 }
40
41 /*
42 * Check to see if this interrupt came from the device:
43 */
44 if ((isr & CISS_ISR_BIT_SIMPLE_INTR) == 0) {
45 smrt->smrt_stats.smrts_unclaimed_interrupts++;
46 smrt->smrt_last_interrupt_unclaimed = now;
47
48 /*
49 * Check to see if the firmware has come to rest. If it has,
50 * this routine will panic the system.
51 */
52 smrt_lockup_check(smrt);
53
54 mutex_exit(&smrt->smrt_mutex);
55 return (DDI_INTR_UNCLAIMED);
56 }
57
58 smrt->smrt_stats.smrts_claimed_interrupts++;
59 smrt->smrt_last_interrupt_claimed = now;
60
61 /*
62 * The interrupt was from our controller, so collect any pending
63 * command completions.
64 */
65 smrt_retrieve_simple(smrt);
66
67 /*
68 * Process any commands in the completion queue.
69 */
70 smrt_process_finishq(smrt);
71
72 mutex_exit(&smrt->smrt_mutex);
73 return (DDI_INTR_CLAIMED);
74 }
75
76 /*
77 * Read tags and process completion of the associated command until the supply
78 * of tags is exhausted.
79 */
80 void
smrt_retrieve_simple(smrt_t * smrt)81 smrt_retrieve_simple(smrt_t *smrt)
82 {
83 uint32_t opq;
84 uint32_t none = 0xffffffff;
85
86 VERIFY(MUTEX_HELD(&smrt->smrt_mutex));
87
88 while ((opq = smrt_get32(smrt, CISS_I2O_OUTBOUND_POST_Q)) != none) {
89 uint32_t tag = CISS_OPQ_READ_TAG(opq);
90 smrt_command_t *smcm;
91
92 if ((smcm = smrt_lookup_inflight(smrt, tag)) == NULL) {
93 dev_err(smrt->smrt_dip, CE_WARN, "spurious tag %x",
94 tag);
95 continue;
96 }
97
98 avl_remove(&smrt->smrt_inflight, smcm);
99 smcm->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT;
100 if (CISS_OPQ_READ_ERROR(opq) != 0) {
101 smcm->smcm_status |= SMRT_CMD_STATUS_ERROR;
102 }
103 smcm->smcm_time_complete = gethrtime();
104
105 /*
106 * Push this command onto the completion queue.
107 */
108 list_insert_tail(&smrt->smrt_finishq, smcm);
109 }
110 }
111
112 /*
113 * Submit a command to the controller by posting it to the Inbound Post Queue
114 * Register.
115 */
116 void
smrt_submit_simple(smrt_t * smrt,smrt_command_t * smcm)117 smrt_submit_simple(smrt_t *smrt, smrt_command_t *smcm)
118 {
119 smrt_put32(smrt, CISS_I2O_INBOUND_POST_Q, smcm->smcm_pa_cmd);
120 }
121
122 /*
123 * Submit a command to the controller by posting it to the Inbound Post Queue
124 * Register. Immediately begin polling on the completion of that command.
125 *
126 * NOTE: This function is for controller initialisation only. It discards
127 * completions of commands other than the expected command as spurious, and
128 * will not interact correctly with the rest of the driver once it is running.
129 */
130 int
smrt_preinit_command_simple(smrt_t * smrt,smrt_command_t * smcm)131 smrt_preinit_command_simple(smrt_t *smrt, smrt_command_t *smcm)
132 {
133 /*
134 * The controller must be initialised to use the Simple Transport
135 * Method, but not be marked RUNNING. The command to process must be a
136 * PREINIT command with the expected tag number, marked for polling.
137 */
138 VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_SIMPLE);
139 VERIFY(!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING));
140 VERIFY(smcm->smcm_type == SMRT_CMDTYPE_PREINIT);
141 VERIFY(smcm->smcm_status & SMRT_CMD_STATUS_POLLED);
142 VERIFY3U(smcm->smcm_tag, ==, SMRT_PRE_TAG_NUMBER);
143
144 /*
145 * Submit this command to the controller.
146 */
147 smcm->smcm_status |= SMRT_CMD_STATUS_INFLIGHT;
148 smrt_put32(smrt, CISS_I2O_INBOUND_POST_Q, smcm->smcm_pa_cmd);
149
150 /*
151 * Poll the controller for completions until we see the command we just
152 * sent, or the timeout expires.
153 */
154 for (;;) {
155 uint32_t none = 0xffffffff;
156 uint32_t opq = smrt_get32(smrt, CISS_I2O_OUTBOUND_POST_Q);
157 uint32_t tag;
158
159 if (smcm->smcm_expiry != 0) {
160 /*
161 * This command has an expiry time. Check to see
162 * if it has already passed:
163 */
164 if (smcm->smcm_expiry < gethrtime()) {
165 return (ETIMEDOUT);
166 }
167 }
168
169 if (opq == none) {
170 delay(drv_usectohz(10 * 1000));
171 continue;
172 }
173
174 if ((tag = CISS_OPQ_READ_TAG(opq)) != SMRT_PRE_TAG_NUMBER) {
175 dev_err(smrt->smrt_dip, CE_WARN, "unexpected tag 0x%x"
176 " completed during driver init", tag);
177 delay(drv_usectohz(10 * 1000));
178 continue;
179 }
180
181 smcm->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT;
182 if (CISS_OPQ_READ_ERROR(opq) != 0) {
183 smcm->smcm_status |= SMRT_CMD_STATUS_ERROR;
184 }
185 smcm->smcm_time_complete = gethrtime();
186 smcm->smcm_status |= SMRT_CMD_STATUS_POLL_COMPLETE;
187
188 return (0);
189 }
190 }
191
192 int
smrt_ctlr_init_simple(smrt_t * smrt)193 smrt_ctlr_init_simple(smrt_t *smrt)
194 {
195 VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_UNKNOWN);
196
197 if (smrt_cfgtbl_transport_has_support(smrt,
198 CISS_CFGTBL_XPORT_SIMPLE) != DDI_SUCCESS) {
199 return (DDI_FAILURE);
200 }
201 smrt->smrt_ctlr_mode = SMRT_CTLR_MODE_SIMPLE;
202
203 /*
204 * Disable device interrupts while we are setting up.
205 */
206 smrt_intr_set(smrt, B_FALSE);
207
208 if ((smrt->smrt_maxcmds = smrt_ctlr_get_cmdsoutmax(smrt)) == 0) {
209 dev_err(smrt->smrt_dip, CE_WARN, "maximum outstanding "
210 "commands set to zero");
211 return (DDI_FAILURE);
212 }
213
214 /*
215 * Determine the number of Scatter/Gather List entries this controller
216 * supports. The maximum number we allow is CISS_MAXSGENTRIES: the
217 * number of elements in the static struct we use for command
218 * submission.
219 */
220 if ((smrt->smrt_sg_cnt = smrt_ctlr_get_maxsgelements(smrt)) == 0) {
221 /*
222 * The CISS specification states that if this value is
223 * zero, we should assume a value of 31 for compatibility
224 * with older firmware.
225 */
226 smrt->smrt_sg_cnt = CISS_SGCNT_FALLBACK;
227
228 } else if (smrt->smrt_sg_cnt > CISS_MAXSGENTRIES) {
229 /*
230 * If the controller supports more than we have allocated,
231 * just cap the count at the allocation size.
232 */
233 smrt->smrt_sg_cnt = CISS_MAXSGENTRIES;
234 }
235
236 /*
237 * Zero the upper 32 bits of the address in the Controller.
238 */
239 ddi_put32(smrt->smrt_ct_handle, &smrt->smrt_ct->Upper32Addr, 0);
240
241 /*
242 * Set the Transport Method and flush the changes to the
243 * Configuration Table.
244 */
245 smrt_cfgtbl_transport_set(smrt, CISS_CFGTBL_XPORT_SIMPLE);
246 if (smrt_cfgtbl_flush(smrt) != DDI_SUCCESS) {
247 return (DDI_FAILURE);
248 }
249
250 if (smrt_cfgtbl_transport_confirm(smrt,
251 CISS_CFGTBL_XPORT_SIMPLE) != DDI_SUCCESS) {
252 return (DDI_FAILURE);
253 }
254
255 /*
256 * Check the outstanding command cap a second time now that we have
257 * flushed out the new Transport Method. This is entirely defensive;
258 * we do not expect this value to change.
259 */
260 uint32_t check_again = smrt_ctlr_get_cmdsoutmax(smrt);
261 if (check_again != smrt->smrt_maxcmds) {
262 dev_err(smrt->smrt_dip, CE_WARN, "maximum outstanding commands "
263 "changed during initialisation (was %u, now %u)",
264 smrt->smrt_maxcmds, check_again);
265 return (DDI_FAILURE);
266 }
267
268 return (DDI_SUCCESS);
269 }
270
271 void
smrt_ctlr_teardown_simple(smrt_t * smrt)272 smrt_ctlr_teardown_simple(smrt_t *smrt)
273 {
274 VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_SIMPLE);
275
276 /*
277 * Due to the nominal simplicity of the simple mode, we have no
278 * particular teardown to perform as we do not allocate anything
279 * on the way up.
280 */
281 smrt->smrt_ctlr_mode = SMRT_CTLR_MODE_UNKNOWN;
282 }
283