1 | /* $NetBSD: mpt.c,v 1.17 2014/09/27 16:14:16 jmcneill Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2000, 2001 by Greg Ansley |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice immediately at the beginning of the file, without modification, |
11 | * this list of conditions, and the following disclaimer. |
12 | * 2. The name of the author may not be used to endorse or promote products |
13 | * derived from this software without specific prior written permission. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR |
19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 | * SUCH DAMAGE. |
26 | */ |
27 | /* |
28 | * Additional Copyright (c) 2002 by Matthew Jacob under same license. |
29 | */ |
30 | /*- |
31 | * Copyright (c) 2002, 2006 by Matthew Jacob |
32 | * All rights reserved. |
33 | * |
34 | * Redistribution and use in source and binary forms, with or without |
35 | * modification, are permitted provided that the following conditions are |
36 | * met: |
37 | * 1. Redistributions of source code must retain the above copyright |
38 | * notice, this list of conditions and the following disclaimer. |
39 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
40 | * substantially similar to the "NO WARRANTY" disclaimer below |
41 | * ("Disclaimer") and any redistribution must be conditioned upon including |
42 | * a substantially similar Disclaimer requirement for further binary |
43 | * redistribution. |
44 | * 3. Neither the names of the above listed copyright holders nor the names |
45 | * of any contributors may be used to endorse or promote products derived |
46 | * from this software without specific prior written permission. |
47 | * |
48 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
49 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
52 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
53 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
54 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
55 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
56 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
57 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT |
58 | * OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
59 | * |
60 | * Support from Chris Ellsworth in order to make SAS adapters work |
61 | * is gratefully acknowledged. |
62 | * |
63 | * |
64 | * Support from LSI-Logic has also gone a great deal toward making this a |
65 | * workable subsystem and is gratefully acknowledged. |
66 | */ |
67 | /*- |
68 | * Copyright (c) 2004, Avid Technology, Inc. and its contributors. |
69 | * Copyright (c) 2005, WHEEL Sp. z o.o. |
70 | * Copyright (c) 2004, 2005 Justin T. Gibbs |
71 | * All rights reserved. |
72 | * |
73 | * Redistribution and use in source and binary forms, with or without |
74 | * modification, are permitted provided that the following conditions are |
75 | * met: |
76 | * 1. Redistributions of source code must retain the above copyright |
77 | * notice, this list of conditions and the following disclaimer. |
78 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
79 | * substantially similar to the "NO WARRANTY" disclaimer below |
80 | * ("Disclaimer") and any redistribution must be conditioned upon including |
81 | * a substantially similar Disclaimer requirement for further binary |
82 | * redistribution. |
83 | * 3. Neither the names of the above listed copyright holders nor the names |
84 | * of any contributors may be used to endorse or promote products derived |
85 | * from this software without specific prior written permission. |
86 | * |
87 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
88 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
89 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
90 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
91 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
92 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
93 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
94 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
95 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
96 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT |
97 | * OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
98 | */ |
99 | |
100 | |
101 | /* |
102 | * mpt.c: |
103 | * |
104 | * Generic routines for LSI Fusion adapters. |
105 | * |
106 | * Adapted from the FreeBSD "mpt" driver by Jason R. Thorpe for |
107 | * Wasabi Systems, Inc. |
108 | * |
109 | * Additional contributions by Garrett D'Amore on behalf of TELES AG. |
110 | */ |
111 | |
112 | #include <sys/cdefs.h> |
113 | __KERNEL_RCSID(0, "$NetBSD: mpt.c,v 1.17 2014/09/27 16:14:16 jmcneill Exp $" ); |
114 | |
115 | #include <dev/ic/mpt.h> |
116 | |
117 | #define MPT_MAX_TRYS 3 |
118 | #define MPT_MAX_WAIT 300000 |
119 | |
120 | static int maxwait_ack = 0; |
121 | static int maxwait_int = 0; |
122 | static int maxwait_state = 0; |
123 | |
124 | static inline u_int32_t |
125 | mpt_rd_db(mpt_softc_t *mpt) |
126 | { |
127 | return mpt_read(mpt, MPT_OFFSET_DOORBELL); |
128 | } |
129 | |
130 | static inline u_int32_t |
131 | mpt_rd_intr(mpt_softc_t *mpt) |
132 | { |
133 | return mpt_read(mpt, MPT_OFFSET_INTR_STATUS); |
134 | } |
135 | |
136 | /* Busy wait for a door bell to be read by IOC */ |
137 | static int |
138 | mpt_wait_db_ack(mpt_softc_t *mpt) |
139 | { |
140 | int i; |
141 | for (i=0; i < MPT_MAX_WAIT; i++) { |
142 | if (!MPT_DB_IS_BUSY(mpt_rd_intr(mpt))) { |
143 | maxwait_ack = i > maxwait_ack ? i : maxwait_ack; |
144 | return MPT_OK; |
145 | } |
146 | |
147 | DELAY(100); |
148 | } |
149 | return MPT_FAIL; |
150 | } |
151 | |
152 | /* Busy wait for a door bell interrupt */ |
153 | static int |
154 | mpt_wait_db_int(mpt_softc_t *mpt) |
155 | { |
156 | int i; |
157 | for (i=0; i < MPT_MAX_WAIT; i++) { |
158 | if (MPT_DB_INTR(mpt_rd_intr(mpt))) { |
159 | maxwait_int = i > maxwait_int ? i : maxwait_int; |
160 | return MPT_OK; |
161 | } |
162 | DELAY(100); |
163 | } |
164 | return MPT_FAIL; |
165 | } |
166 | |
167 | /* Wait for IOC to transition to a give state */ |
168 | void |
169 | mpt_check_doorbell(mpt_softc_t *mpt) |
170 | { |
171 | u_int32_t db = mpt_rd_db(mpt); |
172 | if (MPT_STATE(db) != MPT_DB_STATE_RUNNING) { |
173 | mpt_prt(mpt, "Device not running" ); |
174 | mpt_print_db(db); |
175 | } |
176 | } |
177 | |
178 | /* Wait for IOC to transition to a give state */ |
179 | static int |
180 | mpt_wait_state(mpt_softc_t *mpt, enum DB_STATE_BITS state) |
181 | { |
182 | int i; |
183 | |
184 | for (i = 0; i < MPT_MAX_WAIT; i++) { |
185 | u_int32_t db = mpt_rd_db(mpt); |
186 | if (MPT_STATE(db) == state) { |
187 | maxwait_state = i > maxwait_state ? i : maxwait_state; |
188 | return (MPT_OK); |
189 | } |
190 | DELAY(100); |
191 | } |
192 | return (MPT_FAIL); |
193 | } |
194 | |
195 | |
196 | /* Issue the reset COMMAND to the IOC */ |
197 | int |
198 | mpt_soft_reset(mpt_softc_t *mpt) |
199 | { |
200 | if (mpt->verbose) { |
201 | mpt_prt(mpt, "soft reset" ); |
202 | } |
203 | |
204 | /* Have to use hard reset if we are not in Running state */ |
205 | if (MPT_STATE(mpt_rd_db(mpt)) != MPT_DB_STATE_RUNNING) { |
206 | mpt_prt(mpt, "soft reset failed: device not running" ); |
207 | return MPT_FAIL; |
208 | } |
209 | |
210 | /* If door bell is in use we don't have a chance of getting |
211 | * a word in since the IOC probably crashed in message |
212 | * processing. So don't waste our time. |
213 | */ |
214 | if (MPT_DB_IS_IN_USE(mpt_rd_db(mpt))) { |
215 | mpt_prt(mpt, "soft reset failed: doorbell wedged" ); |
216 | return MPT_FAIL; |
217 | } |
218 | |
219 | /* Send the reset request to the IOC */ |
220 | mpt_write(mpt, MPT_OFFSET_DOORBELL, |
221 | MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET << MPI_DOORBELL_FUNCTION_SHIFT); |
222 | if (mpt_wait_db_ack(mpt) != MPT_OK) { |
223 | mpt_prt(mpt, "soft reset failed: ack timeout" ); |
224 | return MPT_FAIL; |
225 | } |
226 | |
227 | /* Wait for the IOC to reload and come out of reset state */ |
228 | if (mpt_wait_state(mpt, MPT_DB_STATE_READY) != MPT_OK) { |
229 | mpt_prt(mpt, "soft reset failed: device did not start running" ); |
230 | return MPT_FAIL; |
231 | } |
232 | |
233 | return MPT_OK; |
234 | } |
235 | |
236 | /* This is a magic diagnostic reset that resets all the ARM |
237 | * processors in the chip. |
238 | */ |
239 | void |
240 | mpt_hard_reset(mpt_softc_t *mpt) |
241 | { |
242 | if (mpt->verbose) { |
243 | mpt_prt(mpt, "hard reset" ); |
244 | } |
245 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, 0xff); |
246 | |
247 | /* Enable diagnostic registers */ |
248 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_1); |
249 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_2); |
250 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_3); |
251 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_4); |
252 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_5); |
253 | |
254 | /* Diag. port is now active so we can now hit the reset bit */ |
255 | mpt_write(mpt, MPT_OFFSET_DIAGNOSTIC, MPT_DIAG_RESET_IOC); |
256 | |
257 | DELAY(10000); |
258 | |
259 | /* Disable Diagnostic Register */ |
260 | mpt_write(mpt, MPT_OFFSET_SEQUENCE, 0xFF); |
261 | } |
262 | |
263 | /* |
264 | * Reset the IOC when needed. Try software command first then if needed |
265 | * poke at the magic diagnostic reset. Note that a hard reset resets |
266 | * *both* IOCs on dual function chips (FC929 && LSI1030) as well as |
267 | * fouls up the PCI configuration registers. |
268 | */ |
269 | int |
270 | mpt_reset(mpt_softc_t *mpt) |
271 | { |
272 | int ret; |
273 | |
274 | /* Try a soft reset */ |
275 | if ((ret = mpt_soft_reset(mpt)) != MPT_OK) { |
276 | /* Failed; do a hard reset */ |
277 | mpt_hard_reset(mpt); |
278 | |
279 | /* Wait for the IOC to reload and come out of reset state */ |
280 | ret = mpt_wait_state(mpt, MPT_DB_STATE_READY); |
281 | if (ret != MPT_OK) { |
282 | mpt_prt(mpt, "failed to reset device" ); |
283 | } |
284 | } |
285 | |
286 | return ret; |
287 | } |
288 | |
289 | /* Return a command buffer to the free queue */ |
290 | void |
291 | mpt_free_request(mpt_softc_t *mpt, request_t *req) |
292 | { |
293 | if (req == NULL || req != &mpt->request_pool[req->index]) { |
294 | panic("mpt_free_request bad req ptr\n" ); |
295 | return; |
296 | } |
297 | req->sequence = 0; |
298 | req->xfer = NULL; |
299 | req->debug = REQ_FREE; |
300 | SLIST_INSERT_HEAD(&mpt->request_free_list, req, link); |
301 | } |
302 | |
303 | /* Get a command buffer from the free queue */ |
304 | request_t * |
305 | mpt_get_request(mpt_softc_t *mpt) |
306 | { |
307 | request_t *req; |
308 | req = SLIST_FIRST(&mpt->request_free_list); |
309 | if (req != NULL) { |
310 | if (req != &mpt->request_pool[req->index]) { |
311 | panic("mpt_get_request: corrupted request free list\n" ); |
312 | } |
313 | if (req->xfer != NULL) { |
314 | panic("mpt_get_request: corrupted request free list (xfer)\n" ); |
315 | } |
316 | SLIST_REMOVE_HEAD(&mpt->request_free_list, link); |
317 | req->debug = REQ_IN_PROGRESS; |
318 | } |
319 | return req; |
320 | } |
321 | |
322 | /* Pass the command to the IOC */ |
323 | void |
324 | mpt_send_cmd(mpt_softc_t *mpt, request_t *req) |
325 | { |
326 | req->sequence = mpt->sequence++; |
327 | if (mpt->verbose > 1) { |
328 | u_int32_t *pReq; |
329 | pReq = req->req_vbuf; |
330 | mpt_prt(mpt, "Send Request %d (0x%x):" , |
331 | req->index, req->req_pbuf); |
332 | mpt_prt(mpt, "%08x %08x %08x %08x" , |
333 | pReq[0], pReq[1], pReq[2], pReq[3]); |
334 | mpt_prt(mpt, "%08x %08x %08x %08x" , |
335 | pReq[4], pReq[5], pReq[6], pReq[7]); |
336 | mpt_prt(mpt, "%08x %08x %08x %08x" , |
337 | pReq[8], pReq[9], pReq[10], pReq[11]); |
338 | mpt_prt(mpt, "%08x %08x %08x %08x" , |
339 | pReq[12], pReq[13], pReq[14], pReq[15]); |
340 | } |
341 | MPT_SYNC_REQ(mpt, req, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); |
342 | req->debug = REQ_ON_CHIP; |
343 | mpt_write(mpt, MPT_OFFSET_REQUEST_Q, (u_int32_t) req->req_pbuf); |
344 | } |
345 | |
346 | /* |
347 | * Give the reply buffer back to the IOC after we have |
348 | * finished processing it. |
349 | */ |
350 | void |
351 | mpt_free_reply(mpt_softc_t *mpt, u_int32_t ptr) |
352 | { |
353 | mpt_write(mpt, MPT_OFFSET_REPLY_Q, ptr); |
354 | } |
355 | |
356 | /* Get a reply from the IOC */ |
357 | u_int32_t |
358 | mpt_pop_reply_queue(mpt_softc_t *mpt) |
359 | { |
360 | return mpt_read(mpt, MPT_OFFSET_REPLY_Q); |
361 | } |
362 | |
363 | /* |
364 | * Send a command to the IOC via the handshake register. |
365 | * |
366 | * Only done at initialization time and for certain unusual |
367 | * commands such as device/bus reset as specified by LSI. |
368 | */ |
369 | int |
370 | mpt_send_handshake_cmd(mpt_softc_t *mpt, size_t len, void *cmd) |
371 | { |
372 | int i; |
373 | u_int32_t data, *data32; |
374 | |
375 | /* Check condition of the IOC */ |
376 | data = mpt_rd_db(mpt); |
377 | if (((MPT_STATE(data) != MPT_DB_STATE_READY) && |
378 | (MPT_STATE(data) != MPT_DB_STATE_RUNNING) && |
379 | (MPT_STATE(data) != MPT_DB_STATE_FAULT)) || |
380 | ( MPT_DB_IS_IN_USE(data) )) { |
381 | mpt_prt(mpt, "handshake aborted due to invalid doorbell state" ); |
382 | mpt_print_db(data); |
383 | return(EBUSY); |
384 | } |
385 | |
386 | /* We move things in 32 bit chunks */ |
387 | len = (len + 3) >> 2; |
388 | data32 = cmd; |
389 | |
390 | /* Clear any left over pending doorbell interrupts */ |
391 | if (MPT_DB_INTR(mpt_rd_intr(mpt))) |
392 | mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); |
393 | |
394 | /* |
395 | * Tell the handshake reg. we are going to send a command |
396 | * and how long it is going to be. |
397 | */ |
398 | data = (MPI_FUNCTION_HANDSHAKE << MPI_DOORBELL_FUNCTION_SHIFT) | |
399 | (len << MPI_DOORBELL_ADD_DWORDS_SHIFT); |
400 | mpt_write(mpt, MPT_OFFSET_DOORBELL, data); |
401 | |
402 | /* Wait for the chip to notice */ |
403 | if (mpt_wait_db_int(mpt) != MPT_OK) { |
404 | mpt_prt(mpt, "mpt_send_handshake_cmd timeout1" ); |
405 | return ETIMEDOUT; |
406 | } |
407 | |
408 | /* Clear the interrupt */ |
409 | mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); |
410 | |
411 | if (mpt_wait_db_ack(mpt) != MPT_OK) { |
412 | mpt_prt(mpt, "mpt_send_handshake_cmd timeout2" ); |
413 | return ETIMEDOUT; |
414 | } |
415 | |
416 | /* Send the command */ |
417 | for (i = 0; i < len; i++) { |
418 | mpt_write(mpt, MPT_OFFSET_DOORBELL, htole32(*data32++)); |
419 | if (mpt_wait_db_ack(mpt) != MPT_OK) { |
420 | mpt_prt(mpt, |
421 | "mpt_send_handshake_cmd timeout! index = %d" , i); |
422 | return ETIMEDOUT; |
423 | } |
424 | } |
425 | return MPT_OK; |
426 | } |
427 | |
428 | /* Get the response from the handshake register */ |
429 | int |
430 | mpt_recv_handshake_reply(mpt_softc_t *mpt, size_t reply_len, void *reply) |
431 | { |
432 | int left, reply_left; |
433 | u_int16_t *data16; |
434 | MSG_DEFAULT_REPLY *hdr; |
435 | |
436 | /* We move things out in 16 bit chunks */ |
437 | reply_len >>= 1; |
438 | data16 = (u_int16_t *)reply; |
439 | |
440 | hdr = (MSG_DEFAULT_REPLY *)reply; |
441 | |
442 | /* Get first word */ |
443 | if (mpt_wait_db_int(mpt) != MPT_OK) { |
444 | mpt_prt(mpt, "mpt_recv_handshake_cmd timeout1" ); |
445 | return ETIMEDOUT; |
446 | } |
447 | *data16++ = le16toh(mpt_read(mpt, MPT_OFFSET_DOORBELL) & |
448 | MPT_DB_DATA_MASK); |
449 | mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); |
450 | |
451 | /* Get Second Word */ |
452 | if (mpt_wait_db_int(mpt) != MPT_OK) { |
453 | mpt_prt(mpt, "mpt_recv_handshake_cmd timeout2" ); |
454 | return ETIMEDOUT; |
455 | } |
456 | *data16++ = le16toh(mpt_read(mpt, MPT_OFFSET_DOORBELL) & |
457 | MPT_DB_DATA_MASK); |
458 | mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); |
459 | |
460 | /* With the second word, we can now look at the length */ |
461 | if (mpt->verbose > 1 && ((reply_len >> 1) != hdr->MsgLength)) { |
462 | mpt_prt(mpt, "reply length does not match message length: " |
463 | "got 0x%02x, expected 0x%02x" , |
464 | hdr->MsgLength << 2, reply_len << 1); |
465 | } |
466 | |
467 | /* Get rest of the reply; but don't overflow the provided buffer */ |
468 | left = (hdr->MsgLength << 1) - 2; |
469 | reply_left = reply_len - 2; |
470 | while (left--) { |
471 | u_int16_t datum; |
472 | |
473 | if (mpt_wait_db_int(mpt) != MPT_OK) { |
474 | mpt_prt(mpt, "mpt_recv_handshake_cmd timeout3" ); |
475 | return ETIMEDOUT; |
476 | } |
477 | datum = mpt_read(mpt, MPT_OFFSET_DOORBELL); |
478 | |
479 | if (reply_left-- > 0) |
480 | *data16++ = le16toh(datum & MPT_DB_DATA_MASK); |
481 | |
482 | mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); |
483 | } |
484 | |
485 | /* One more wait & clear at the end */ |
486 | if (mpt_wait_db_int(mpt) != MPT_OK) { |
487 | mpt_prt(mpt, "mpt_recv_handshake_cmd timeout4" ); |
488 | return ETIMEDOUT; |
489 | } |
490 | mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); |
491 | |
492 | if ((hdr->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { |
493 | if (mpt->verbose > 1) |
494 | mpt_print_reply(hdr); |
495 | return (MPT_FAIL | hdr->IOCStatus); |
496 | } |
497 | |
498 | return (0); |
499 | } |
500 | |
501 | static int |
502 | mpt_get_iocfacts(mpt_softc_t *mpt, MSG_IOC_FACTS_REPLY *freplp) |
503 | { |
504 | MSG_IOC_FACTS f_req; |
505 | int error; |
506 | |
507 | memset(&f_req, 0, sizeof f_req); |
508 | f_req.Function = MPI_FUNCTION_IOC_FACTS; |
509 | f_req.MsgContext = htole32(0x12071942); |
510 | error = mpt_send_handshake_cmd(mpt, sizeof f_req, &f_req); |
511 | if (error) |
512 | return(error); |
513 | error = mpt_recv_handshake_reply(mpt, sizeof (*freplp), freplp); |
514 | return (error); |
515 | } |
516 | |
517 | static int |
518 | mpt_get_portfacts(mpt_softc_t *mpt, MSG_PORT_FACTS_REPLY *freplp) |
519 | { |
520 | MSG_PORT_FACTS f_req; |
521 | int error; |
522 | |
523 | /* XXX: Only getting PORT FACTS for Port 0 */ |
524 | memset(&f_req, 0, sizeof f_req); |
525 | f_req.Function = MPI_FUNCTION_PORT_FACTS; |
526 | f_req.MsgContext = htole32(0x12071943); |
527 | error = mpt_send_handshake_cmd(mpt, sizeof f_req, &f_req); |
528 | if (error) |
529 | return(error); |
530 | error = mpt_recv_handshake_reply(mpt, sizeof (*freplp), freplp); |
531 | return (error); |
532 | } |
533 | |
534 | /* |
535 | * Send the initialization request. This is where we specify how many |
536 | * SCSI busses and how many devices per bus we wish to emulate. |
537 | * This is also the command that specifies the max size of the reply |
538 | * frames from the IOC that we will be allocating. |
539 | */ |
540 | static int |
541 | mpt_send_ioc_init(mpt_softc_t *mpt, u_int32_t who) |
542 | { |
543 | int error = 0; |
544 | MSG_IOC_INIT init; |
545 | MSG_IOC_INIT_REPLY reply; |
546 | |
547 | memset(&init, 0, sizeof init); |
548 | init.WhoInit = who; |
549 | init.Function = MPI_FUNCTION_IOC_INIT; |
550 | init.MaxDevices = mpt->mpt_max_devices; |
551 | init.MaxBuses = 1; |
552 | init.ReplyFrameSize = htole16(MPT_REPLY_SIZE); |
553 | init.MsgContext = htole32(0x12071941); |
554 | |
555 | if ((error = mpt_send_handshake_cmd(mpt, sizeof init, &init)) != 0) { |
556 | return(error); |
557 | } |
558 | |
559 | error = mpt_recv_handshake_reply(mpt, sizeof reply, &reply); |
560 | return (error); |
561 | } |
562 | |
563 | |
564 | /* |
565 | * Utiltity routine to read configuration headers and pages |
566 | */ |
567 | |
568 | int |
569 | (mpt_softc_t *mpt, int PageType, int PageNumber, |
570 | int PageAddress, fCONFIG_PAGE_HEADER *rslt) |
571 | { |
572 | int count; |
573 | request_t *req; |
574 | MSG_CONFIG *cfgp; |
575 | MSG_CONFIG_REPLY *reply; |
576 | |
577 | req = mpt_get_request(mpt); |
578 | |
579 | cfgp = req->req_vbuf; |
580 | memset(cfgp, 0, sizeof *cfgp); |
581 | |
582 | cfgp->Action = MPI_CONFIG_ACTION_PAGE_HEADER; |
583 | cfgp->Function = MPI_FUNCTION_CONFIG; |
584 | cfgp->Header.PageNumber = (U8) PageNumber; |
585 | cfgp->Header.PageType = (U8) PageType; |
586 | cfgp->PageAddress = htole32(PageAddress); |
587 | MPI_pSGE_SET_FLAGS(((SGE_SIMPLE32 *) &cfgp->PageBufferSGE), |
588 | (MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | |
589 | MPI_SGE_FLAGS_SIMPLE_ELEMENT | MPI_SGE_FLAGS_END_OF_LIST)); |
590 | cfgp->MsgContext = htole32(req->index | 0x80000000); |
591 | |
592 | mpt_check_doorbell(mpt); |
593 | mpt_send_cmd(mpt, req); |
594 | count = 0; |
595 | do { |
596 | DELAY(500); |
597 | mpt_intr(mpt); |
598 | if (++count == 1000) { |
599 | mpt_prt(mpt, "read_cfg_header timed out" ); |
600 | return (-1); |
601 | } |
602 | } while (req->debug == REQ_ON_CHIP); |
603 | |
604 | reply = (MSG_CONFIG_REPLY *) MPT_REPLY_PTOV(mpt, req->sequence); |
605 | if ((reply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { |
606 | mpt_prt(mpt, "mpt_read_cfg_header: Config Info Status %x" , |
607 | reply->IOCStatus); |
608 | mpt_free_reply(mpt, (req->sequence << 1)); |
609 | return (-1); |
610 | } |
611 | memcpy(rslt, &reply->Header, sizeof (fCONFIG_PAGE_HEADER)); |
612 | mpt_free_reply(mpt, (req->sequence << 1)); |
613 | mpt_free_request(mpt, req); |
614 | return (0); |
615 | } |
616 | |
617 | #define CFG_DATA_OFF 128 |
618 | |
619 | int |
620 | mpt_read_cfg_page(mpt_softc_t *mpt, int PageAddress, fCONFIG_PAGE_HEADER *hdr) |
621 | { |
622 | int count; |
623 | request_t *req; |
624 | SGE_SIMPLE32 *se; |
625 | MSG_CONFIG *cfgp; |
626 | size_t amt; |
627 | MSG_CONFIG_REPLY *reply; |
628 | |
629 | req = mpt_get_request(mpt); |
630 | |
631 | cfgp = req->req_vbuf; |
632 | memset(cfgp, 0, MPT_REQUEST_AREA); |
633 | cfgp->Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; |
634 | cfgp->Function = MPI_FUNCTION_CONFIG; |
635 | cfgp->Header = *hdr; |
636 | amt = (cfgp->Header.PageLength * sizeof (u_int32_t)); |
637 | cfgp->Header.PageType &= MPI_CONFIG_PAGETYPE_MASK; |
638 | cfgp->PageAddress = htole32(PageAddress); |
639 | se = (SGE_SIMPLE32 *) &cfgp->PageBufferSGE; |
640 | se->Address = htole32(req->req_pbuf + CFG_DATA_OFF); |
641 | MPI_pSGE_SET_LENGTH(se, amt); |
642 | MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT | |
643 | MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | |
644 | MPI_SGE_FLAGS_END_OF_LIST)); |
645 | se->FlagsLength = htole32(se->FlagsLength); |
646 | |
647 | cfgp->MsgContext = htole32(req->index | 0x80000000); |
648 | |
649 | mpt_check_doorbell(mpt); |
650 | mpt_send_cmd(mpt, req); |
651 | count = 0; |
652 | do { |
653 | DELAY(500); |
654 | mpt_intr(mpt); |
655 | if (++count == 1000) { |
656 | mpt_prt(mpt, "read_cfg_page timed out" ); |
657 | return (-1); |
658 | } |
659 | } while (req->debug == REQ_ON_CHIP); |
660 | |
661 | reply = (MSG_CONFIG_REPLY *) MPT_REPLY_PTOV(mpt, req->sequence); |
662 | if ((reply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { |
663 | mpt_prt(mpt, "mpt_read_cfg_page: Config Info Status %x" , |
664 | reply->IOCStatus); |
665 | mpt_free_reply(mpt, (req->sequence << 1)); |
666 | return (-1); |
667 | } |
668 | mpt_free_reply(mpt, (req->sequence << 1)); |
669 | #if 0 /* XXXJRT */ |
670 | bus_dmamap_sync(mpt->request_dmat, mpt->request_dmap, |
671 | BUS_DMASYNC_POSTREAD); |
672 | #endif |
673 | if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && |
674 | cfgp->Header.PageNumber == 0) { |
675 | amt = sizeof (fCONFIG_PAGE_SCSI_PORT_0); |
676 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && |
677 | cfgp->Header.PageNumber == 1) { |
678 | amt = sizeof (fCONFIG_PAGE_SCSI_PORT_1); |
679 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && |
680 | cfgp->Header.PageNumber == 2) { |
681 | amt = sizeof (fCONFIG_PAGE_SCSI_PORT_2); |
682 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && |
683 | cfgp->Header.PageNumber == 0) { |
684 | amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_0); |
685 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && |
686 | cfgp->Header.PageNumber == 1) { |
687 | amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_1); |
688 | } |
689 | memcpy(hdr, (char *)req->req_vbuf + CFG_DATA_OFF, amt); |
690 | mpt_free_request(mpt, req); |
691 | return (0); |
692 | } |
693 | |
694 | int |
695 | mpt_write_cfg_page(mpt_softc_t *mpt, int PageAddress, fCONFIG_PAGE_HEADER *hdr) |
696 | { |
697 | int count, hdr_attr; |
698 | request_t *req; |
699 | SGE_SIMPLE32 *se; |
700 | MSG_CONFIG *cfgp; |
701 | size_t amt; |
702 | MSG_CONFIG_REPLY *reply; |
703 | |
704 | req = mpt_get_request(mpt); |
705 | |
706 | cfgp = req->req_vbuf; |
707 | memset(cfgp, 0, sizeof *cfgp); |
708 | |
709 | hdr_attr = hdr->PageType & MPI_CONFIG_PAGEATTR_MASK; |
710 | if (hdr_attr != MPI_CONFIG_PAGEATTR_CHANGEABLE && |
711 | hdr_attr != MPI_CONFIG_PAGEATTR_PERSISTENT) { |
712 | mpt_prt(mpt, "page type 0x%x not changeable" , |
713 | hdr->PageType & MPI_CONFIG_PAGETYPE_MASK); |
714 | return (-1); |
715 | } |
716 | hdr->PageType &= MPI_CONFIG_PAGETYPE_MASK; |
717 | |
718 | cfgp->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; |
719 | cfgp->Function = MPI_FUNCTION_CONFIG; |
720 | cfgp->Header = *hdr; |
721 | amt = (cfgp->Header.PageLength * sizeof (u_int32_t)); |
722 | cfgp->PageAddress = htole32(PageAddress); |
723 | |
724 | se = (SGE_SIMPLE32 *) &cfgp->PageBufferSGE; |
725 | se->Address = htole32(req->req_pbuf + CFG_DATA_OFF); |
726 | MPI_pSGE_SET_LENGTH(se, amt); |
727 | MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT | |
728 | MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | |
729 | MPI_SGE_FLAGS_END_OF_LIST | MPI_SGE_FLAGS_HOST_TO_IOC)); |
730 | se->FlagsLength = htole32(se->FlagsLength); |
731 | |
732 | cfgp->MsgContext = htole32(req->index | 0x80000000); |
733 | |
734 | if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && |
735 | cfgp->Header.PageNumber == 0) { |
736 | amt = sizeof (fCONFIG_PAGE_SCSI_PORT_0); |
737 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && |
738 | cfgp->Header.PageNumber == 1) { |
739 | amt = sizeof (fCONFIG_PAGE_SCSI_PORT_1); |
740 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && |
741 | cfgp->Header.PageNumber == 2) { |
742 | amt = sizeof (fCONFIG_PAGE_SCSI_PORT_2); |
743 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && |
744 | cfgp->Header.PageNumber == 0) { |
745 | amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_0); |
746 | } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && |
747 | cfgp->Header.PageNumber == 1) { |
748 | amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_1); |
749 | } |
750 | memcpy((char *)req->req_vbuf + CFG_DATA_OFF, hdr, amt); |
751 | /* Restore stripped out attributes */ |
752 | hdr->PageType |= hdr_attr; |
753 | |
754 | mpt_check_doorbell(mpt); |
755 | mpt_send_cmd(mpt, req); |
756 | count = 0; |
757 | do { |
758 | DELAY(500); |
759 | mpt_intr(mpt); |
760 | if (++count == 1000) { |
761 | hdr->PageType |= hdr_attr; |
762 | mpt_prt(mpt, "mpt_write_cfg_page timed out" ); |
763 | return (-1); |
764 | } |
765 | } while (req->debug == REQ_ON_CHIP); |
766 | |
767 | reply = (MSG_CONFIG_REPLY *) MPT_REPLY_PTOV(mpt, req->sequence); |
768 | if ((reply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { |
769 | mpt_prt(mpt, "mpt_write_cfg_page: Config Info Status %x" , |
770 | le16toh(reply->IOCStatus)); |
771 | mpt_free_reply(mpt, (req->sequence << 1)); |
772 | return (-1); |
773 | } |
774 | mpt_free_reply(mpt, (req->sequence << 1)); |
775 | |
776 | mpt_free_request(mpt, req); |
777 | return (0); |
778 | } |
779 | |
780 | /* |
781 | * Read SCSI configuration information |
782 | */ |
783 | static int |
784 | mpt_read_config_info_spi(mpt_softc_t *mpt) |
785 | { |
786 | int rv, i; |
787 | |
788 | rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_PORT, 0, |
789 | 0, &mpt->mpt_port_page0.Header); |
790 | if (rv) { |
791 | return (-1); |
792 | } |
793 | if (mpt->verbose > 1) { |
794 | mpt_prt(mpt, "SPI Port Page 0 Header: %x %x %x %x" , |
795 | mpt->mpt_port_page0.Header.PageVersion, |
796 | mpt->mpt_port_page0.Header.PageLength, |
797 | mpt->mpt_port_page0.Header.PageNumber, |
798 | mpt->mpt_port_page0.Header.PageType); |
799 | } |
800 | |
801 | rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_PORT, 1, |
802 | 0, &mpt->mpt_port_page1.Header); |
803 | if (rv) { |
804 | return (-1); |
805 | } |
806 | if (mpt->verbose > 1) { |
807 | mpt_prt(mpt, "SPI Port Page 1 Header: %x %x %x %x" , |
808 | mpt->mpt_port_page1.Header.PageVersion, |
809 | mpt->mpt_port_page1.Header.PageLength, |
810 | mpt->mpt_port_page1.Header.PageNumber, |
811 | mpt->mpt_port_page1.Header.PageType); |
812 | } |
813 | |
814 | rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_PORT, 2, |
815 | 0, &mpt->mpt_port_page2.Header); |
816 | if (rv) { |
817 | return (-1); |
818 | } |
819 | |
820 | if (mpt->verbose > 1) { |
821 | mpt_prt(mpt, "SPI Port Page 2 Header: %x %x %x %x" , |
822 | mpt->mpt_port_page1.Header.PageVersion, |
823 | mpt->mpt_port_page1.Header.PageLength, |
824 | mpt->mpt_port_page1.Header.PageNumber, |
825 | mpt->mpt_port_page1.Header.PageType); |
826 | } |
827 | |
828 | for (i = 0; i < 16; i++) { |
829 | rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, |
830 | 0, i, &mpt->mpt_dev_page0[i].Header); |
831 | if (rv) { |
832 | return (-1); |
833 | } |
834 | if (mpt->verbose > 1) { |
835 | mpt_prt(mpt, |
836 | "SPI Target %d Device Page 0 Header: %x %x %x %x" , |
837 | i, mpt->mpt_dev_page0[i].Header.PageVersion, |
838 | mpt->mpt_dev_page0[i].Header.PageLength, |
839 | mpt->mpt_dev_page0[i].Header.PageNumber, |
840 | mpt->mpt_dev_page0[i].Header.PageType); |
841 | } |
842 | |
843 | rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, |
844 | 1, i, &mpt->mpt_dev_page1[i].Header); |
845 | if (rv) { |
846 | return (-1); |
847 | } |
848 | if (mpt->verbose > 1) { |
849 | mpt_prt(mpt, |
850 | "SPI Target %d Device Page 1 Header: %x %x %x %x" , |
851 | i, mpt->mpt_dev_page1[i].Header.PageVersion, |
852 | mpt->mpt_dev_page1[i].Header.PageLength, |
853 | mpt->mpt_dev_page1[i].Header.PageNumber, |
854 | mpt->mpt_dev_page1[i].Header.PageType); |
855 | } |
856 | } |
857 | |
858 | /* |
859 | * At this point, we don't *have* to fail. As long as we have |
860 | * valid config header information, we can (barely) lurch |
861 | * along. |
862 | */ |
863 | |
864 | rv = mpt_read_cfg_page(mpt, 0, &mpt->mpt_port_page0.Header); |
865 | mpt2host_config_page_scsi_port_0(&mpt->mpt_port_page0); |
866 | if (rv) { |
867 | mpt_prt(mpt, "failed to read SPI Port Page 0" ); |
868 | } else if (mpt->verbose > 1) { |
869 | mpt_prt(mpt, |
870 | "SPI Port Page 0: Capabilities %x PhysicalInterface %x" , |
871 | mpt->mpt_port_page0.Capabilities, |
872 | mpt->mpt_port_page0.PhysicalInterface); |
873 | } |
874 | |
875 | rv = mpt_read_cfg_page(mpt, 0, &mpt->mpt_port_page1.Header); |
876 | mpt2host_config_page_scsi_port_1(&mpt->mpt_port_page1); |
877 | if (rv) { |
878 | mpt_prt(mpt, "failed to read SPI Port Page 1" ); |
879 | } else if (mpt->verbose > 1) { |
880 | mpt_prt(mpt, |
881 | "SPI Port Page 1: Configuration %x OnBusTimerValue %x" , |
882 | mpt->mpt_port_page1.Configuration, |
883 | mpt->mpt_port_page1.OnBusTimerValue); |
884 | } |
885 | |
886 | rv = mpt_read_cfg_page(mpt, 0, &mpt->mpt_port_page2.Header); |
887 | mpt2host_config_page_scsi_port_2(&mpt->mpt_port_page2); |
888 | if (rv) { |
889 | mpt_prt(mpt, "failed to read SPI Port Page 2" ); |
890 | } else if (mpt->verbose > 1) { |
891 | mpt_prt(mpt, |
892 | "SPI Port Page 2: Flags %x Settings %x" , |
893 | mpt->mpt_port_page2.PortFlags, |
894 | mpt->mpt_port_page2.PortSettings); |
895 | for (i = 0; i < 1; i++) { |
896 | mpt_prt(mpt, |
897 | "SPI Port Page 2 Tgt %d: timo %x SF %x Flags %x" , |
898 | i, mpt->mpt_port_page2.DeviceSettings[i].Timeout, |
899 | mpt->mpt_port_page2.DeviceSettings[i].SyncFactor, |
900 | mpt->mpt_port_page2.DeviceSettings[i].DeviceFlags); |
901 | } |
902 | } |
903 | |
904 | for (i = 0; i < 16; i++) { |
905 | rv = mpt_read_cfg_page(mpt, i, &mpt->mpt_dev_page0[i].Header); |
906 | mpt2host_config_page_scsi_device_0(&mpt->mpt_dev_page0[i]); |
907 | if (rv) { |
908 | mpt_prt(mpt, "cannot read SPI Tgt %d Device Page 0" , i); |
909 | continue; |
910 | } |
911 | if (mpt->verbose > 1) { |
912 | mpt_prt(mpt, |
913 | "SPI Tgt %d Page 0: NParms %x Information %x" , |
914 | i, mpt->mpt_dev_page0[i].NegotiatedParameters, |
915 | mpt->mpt_dev_page0[i].Information); |
916 | } |
917 | rv = mpt_read_cfg_page(mpt, i, &mpt->mpt_dev_page1[i].Header); |
918 | mpt2host_config_page_scsi_device_1(&mpt->mpt_dev_page1[i]); |
919 | if (rv) { |
920 | mpt_prt(mpt, "cannot read SPI Tgt %d Device Page 1" , i); |
921 | continue; |
922 | } |
923 | if (mpt->verbose > 1) { |
924 | mpt_prt(mpt, |
925 | "SPI Tgt %d Page 1: RParms %x Configuration %x" , |
926 | i, mpt->mpt_dev_page1[i].RequestedParameters, |
927 | mpt->mpt_dev_page1[i].Configuration); |
928 | } |
929 | } |
930 | return (0); |
931 | } |
932 | |
933 | /* |
934 | * Validate SPI configuration information. |
935 | * |
936 | * In particular, validate SPI Port Page 1. |
937 | */ |
938 | static int |
939 | mpt_set_initial_config_spi(mpt_softc_t *mpt) |
940 | { |
941 | int i, pp1val = ((1 << mpt->mpt_ini_id) << 16) | mpt->mpt_ini_id; |
942 | |
943 | mpt->mpt_disc_enable = 0xff; |
944 | mpt->mpt_tag_enable = 0; |
945 | |
946 | if (mpt->mpt_port_page1.Configuration != pp1val) { |
947 | fCONFIG_PAGE_SCSI_PORT_1 tmp; |
948 | |
949 | mpt_prt(mpt, |
950 | "SPI Port Page 1 Config value bad (%x)- should be %x" , |
951 | mpt->mpt_port_page1.Configuration, pp1val); |
952 | tmp = mpt->mpt_port_page1; |
953 | tmp.Configuration = pp1val; |
954 | host2mpt_config_page_scsi_port_1(&tmp); |
955 | if (mpt_write_cfg_page(mpt, 0, &tmp.Header)) { |
956 | return (-1); |
957 | } |
958 | if (mpt_read_cfg_page(mpt, 0, &tmp.Header)) { |
959 | return (-1); |
960 | } |
961 | mpt2host_config_page_scsi_port_1(&tmp); |
962 | if (tmp.Configuration != pp1val) { |
963 | mpt_prt(mpt, |
964 | "failed to reset SPI Port Page 1 Config value" ); |
965 | return (-1); |
966 | } |
967 | mpt->mpt_port_page1 = tmp; |
968 | } |
969 | |
970 | i = 0; |
971 | for (i = 0; i < 16; i++) { |
972 | fCONFIG_PAGE_SCSI_DEVICE_1 tmp; |
973 | |
974 | tmp = mpt->mpt_dev_page1[i]; |
975 | tmp.RequestedParameters = 0; |
976 | tmp.Configuration = 0; |
977 | if (mpt->verbose > 1) { |
978 | mpt_prt(mpt, |
979 | "Set Tgt %d SPI DevicePage 1 values to %x 0 %x" , |
980 | i, tmp.RequestedParameters, tmp.Configuration); |
981 | } |
982 | host2mpt_config_page_scsi_device_1(&tmp); |
983 | if (mpt_write_cfg_page(mpt, i, &tmp.Header)) { |
984 | return (-1); |
985 | } |
986 | if (mpt_read_cfg_page(mpt, i, &tmp.Header)) { |
987 | return (-1); |
988 | } |
989 | mpt2host_config_page_scsi_device_1(&tmp); |
990 | mpt->mpt_dev_page1[i] = tmp; |
991 | if (mpt->verbose > 1) { |
992 | mpt_prt(mpt, |
993 | "SPI Tgt %d Page 1: RParm %x Configuration %x" , i, |
994 | mpt->mpt_dev_page1[i].RequestedParameters, |
995 | mpt->mpt_dev_page1[i].Configuration); |
996 | } |
997 | } |
998 | return (0); |
999 | } |
1000 | |
1001 | /* |
1002 | * Enable IOC port |
1003 | */ |
1004 | static int |
1005 | mpt_send_port_enable(mpt_softc_t *mpt, int port) |
1006 | { |
1007 | int count; |
1008 | request_t *req; |
1009 | MSG_PORT_ENABLE *enable_req; |
1010 | |
1011 | req = mpt_get_request(mpt); |
1012 | |
1013 | enable_req = req->req_vbuf; |
1014 | memset(enable_req, 0, sizeof *enable_req); |
1015 | |
1016 | enable_req->Function = MPI_FUNCTION_PORT_ENABLE; |
1017 | enable_req->MsgContext = htole32(req->index | 0x80000000); |
1018 | enable_req->PortNumber = port; |
1019 | |
1020 | mpt_check_doorbell(mpt); |
1021 | if (mpt->verbose > 1) { |
1022 | mpt_prt(mpt, "enabling port %d" , port); |
1023 | } |
1024 | mpt_send_cmd(mpt, req); |
1025 | |
1026 | count = 0; |
1027 | do { |
1028 | DELAY(500); |
1029 | mpt_intr(mpt); |
1030 | if (++count == 100000) { |
1031 | mpt_prt(mpt, "port enable timed out" ); |
1032 | return (-1); |
1033 | } |
1034 | } while (req->debug == REQ_ON_CHIP); |
1035 | mpt_free_request(mpt, req); |
1036 | return (0); |
1037 | } |
1038 | |
1039 | /* |
1040 | * Enable/Disable asynchronous event reporting. |
1041 | * |
1042 | * NB: this is the first command we send via shared memory |
1043 | * instead of the handshake register. |
1044 | */ |
1045 | static int |
1046 | mpt_send_event_request(mpt_softc_t *mpt, int onoff) |
1047 | { |
1048 | request_t *req; |
1049 | MSG_EVENT_NOTIFY *enable_req; |
1050 | |
1051 | req = mpt_get_request(mpt); |
1052 | |
1053 | enable_req = req->req_vbuf; |
1054 | memset(enable_req, 0, sizeof *enable_req); |
1055 | |
1056 | enable_req->Function = MPI_FUNCTION_EVENT_NOTIFICATION; |
1057 | enable_req->MsgContext = htole32(req->index | 0x80000000); |
1058 | enable_req->Switch = onoff; |
1059 | |
1060 | mpt_check_doorbell(mpt); |
1061 | if (mpt->verbose > 1) { |
1062 | mpt_prt(mpt, "%sabling async events" , onoff? "en" : "dis" ); |
1063 | } |
1064 | mpt_send_cmd(mpt, req); |
1065 | |
1066 | return (0); |
1067 | } |
1068 | |
1069 | /* |
1070 | * Un-mask the interrupts on the chip. |
1071 | */ |
1072 | void |
1073 | mpt_enable_ints(mpt_softc_t *mpt) |
1074 | { |
1075 | /* Unmask every thing except door bell int */ |
1076 | mpt_write(mpt, MPT_OFFSET_INTR_MASK, MPT_INTR_DB_MASK); |
1077 | } |
1078 | |
1079 | /* |
1080 | * Mask the interrupts on the chip. |
1081 | */ |
1082 | void |
1083 | mpt_disable_ints(mpt_softc_t *mpt) |
1084 | { |
1085 | /* Mask all interrupts */ |
1086 | mpt_write(mpt, MPT_OFFSET_INTR_MASK, |
1087 | MPT_INTR_REPLY_MASK | MPT_INTR_DB_MASK); |
1088 | } |
1089 | |
1090 | /* (Re)Initialize the chip for use */ |
1091 | int |
1092 | mpt_hw_init(mpt_softc_t *mpt) |
1093 | { |
1094 | u_int32_t db; |
1095 | int try; |
1096 | |
1097 | /* |
1098 | * Start by making sure we're not at FAULT or RESET state |
1099 | */ |
1100 | for (try = 0; try < MPT_MAX_TRYS; try++) { |
1101 | |
1102 | db = mpt_rd_db(mpt); |
1103 | |
1104 | switch (MPT_STATE(db)) { |
1105 | case MPT_DB_STATE_READY: |
1106 | return (0); |
1107 | |
1108 | default: |
1109 | /* if peer has already reset us, don't do it again! */ |
1110 | if (MPT_WHO(db) == MPT_DB_INIT_PCIPEER) |
1111 | return (0); |
1112 | /*FALLTHRU*/ |
1113 | case MPT_DB_STATE_RESET: |
1114 | case MPT_DB_STATE_FAULT: |
1115 | if (mpt_reset(mpt) != MPT_OK) { |
1116 | DELAY(10000); |
1117 | continue; |
1118 | } |
1119 | break; |
1120 | } |
1121 | } |
1122 | return (EIO); |
1123 | } |
1124 | |
1125 | int |
1126 | mpt_init(mpt_softc_t *mpt, u_int32_t who) |
1127 | { |
1128 | int try; |
1129 | MSG_IOC_FACTS_REPLY facts; |
1130 | MSG_PORT_FACTS_REPLY pfp; |
1131 | prop_dictionary_t dict; |
1132 | uint32_t ini_id; |
1133 | uint32_t pptr; |
1134 | int val; |
1135 | |
1136 | /* Put all request buffers (back) on the free list */ |
1137 | SLIST_INIT(&mpt->request_free_list); |
1138 | for (val = 0; val < MPT_MAX_REQUESTS(mpt); val++) { |
1139 | mpt_free_request(mpt, &mpt->request_pool[val]); |
1140 | } |
1141 | |
1142 | if (mpt->verbose > 1) { |
1143 | mpt_prt(mpt, "doorbell req = %s" , |
1144 | mpt_ioc_diag(mpt_read(mpt, MPT_OFFSET_DOORBELL))); |
1145 | } |
1146 | |
1147 | /* |
1148 | * Start by making sure we're not at FAULT or RESET state |
1149 | */ |
1150 | if (mpt_hw_init(mpt) != 0) |
1151 | return (EIO); |
1152 | |
1153 | dict = device_properties(mpt->sc_dev); |
1154 | |
1155 | for (try = 0; try < MPT_MAX_TRYS; try++) { |
1156 | /* |
1157 | * No need to reset if the IOC is already in the READY state. |
1158 | */ |
1159 | |
1160 | if (mpt_get_iocfacts(mpt, &facts) != MPT_OK) { |
1161 | mpt_prt(mpt, "mpt_get_iocfacts failed" ); |
1162 | continue; |
1163 | } |
1164 | mpt2host_iocfacts_reply(&facts); |
1165 | |
1166 | if (mpt->verbose > 1) { |
1167 | mpt_prt(mpt, |
1168 | "IOCFACTS: GlobalCredits=%d BlockSize=%u " |
1169 | "Request Frame Size %u" , facts.GlobalCredits, |
1170 | facts.BlockSize, facts.RequestFrameSize); |
1171 | } |
1172 | mpt->mpt_max_devices = facts.MaxDevices; |
1173 | mpt->mpt_global_credits = facts.GlobalCredits; |
1174 | mpt->request_frame_size = facts.RequestFrameSize; |
1175 | |
1176 | if (mpt_get_portfacts(mpt, &pfp) != MPT_OK) { |
1177 | mpt_prt(mpt, "mpt_get_portfacts failed" ); |
1178 | continue; |
1179 | } |
1180 | mpt2host_portfacts_reply(&pfp); |
1181 | |
1182 | if (mpt->verbose > 1) { |
1183 | mpt_prt(mpt, |
1184 | "PORTFACTS: Type %x PFlags %x IID %d MaxDev %d" , |
1185 | pfp.PortType, pfp.ProtocolFlags, pfp.PortSCSIID, |
1186 | pfp.MaxDevices); |
1187 | } |
1188 | |
1189 | if (!(pfp.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) { |
1190 | mpt_prt(mpt, "initiator role unsupported" ); |
1191 | return (ENXIO); |
1192 | } |
1193 | |
1194 | switch (pfp.PortType) { |
1195 | case MPI_PORTFACTS_PORTTYPE_FC: |
1196 | mpt->is_fc = 1; |
1197 | mpt->mpt_max_devices = 255; |
1198 | break; |
1199 | case MPI_PORTFACTS_PORTTYPE_SCSI: |
1200 | mpt->is_scsi = 1; |
1201 | /* some SPI controllers (VMWare, Sun) lie */ |
1202 | mpt->mpt_max_devices = 16; |
1203 | break; |
1204 | case MPI_PORTFACTS_PORTTYPE_SAS: |
1205 | mpt->is_sas = 1; |
1206 | break; |
1207 | default: |
1208 | mpt_prt(mpt, "Unsupported Port Type (%x)" , |
1209 | pfp.PortType); |
1210 | return (ENXIO); |
1211 | } |
1212 | |
1213 | if (!mpt->is_sas && !mpt->is_fc && |
1214 | prop_dictionary_get_uint32(dict, "scsi-initiator-id" , &ini_id)) |
1215 | mpt->mpt_ini_id = ini_id; |
1216 | else |
1217 | mpt->mpt_ini_id = pfp.PortSCSIID; |
1218 | |
1219 | if (mpt_send_ioc_init(mpt, who) != MPT_OK) { |
1220 | mpt_prt(mpt, "mpt_send_ioc_init failed" ); |
1221 | continue; |
1222 | } |
1223 | |
1224 | if (mpt->verbose > 1) { |
1225 | mpt_prt(mpt, "mpt_send_ioc_init ok" ); |
1226 | } |
1227 | |
1228 | if (mpt_wait_state(mpt, MPT_DB_STATE_RUNNING) != MPT_OK) { |
1229 | mpt_prt(mpt, "IOC failed to go to run state" ); |
1230 | continue; |
1231 | } |
1232 | if (mpt->verbose > 1) { |
1233 | mpt_prt(mpt, "IOC now at RUNSTATE" ); |
1234 | } |
1235 | |
1236 | /* |
1237 | * Give it reply buffers |
1238 | * |
1239 | * Do *not* except global credits. |
1240 | */ |
1241 | for (val = 0, pptr = mpt->reply_phys; |
1242 | (pptr + MPT_REPLY_SIZE) < (mpt->reply_phys + PAGE_SIZE); |
1243 | pptr += MPT_REPLY_SIZE) { |
1244 | mpt_free_reply(mpt, pptr); |
1245 | if (++val == mpt->mpt_global_credits - 1) |
1246 | break; |
1247 | } |
1248 | |
1249 | /* |
1250 | * Enable asynchronous event reporting |
1251 | */ |
1252 | mpt_send_event_request(mpt, 1); |
1253 | |
1254 | |
1255 | /* |
1256 | * Read set up initial configuration information |
1257 | * (SPI only for now) |
1258 | */ |
1259 | |
1260 | if (mpt->is_scsi) { |
1261 | if (mpt_read_config_info_spi(mpt)) { |
1262 | return (EIO); |
1263 | } |
1264 | if (mpt_set_initial_config_spi(mpt)) { |
1265 | return (EIO); |
1266 | } |
1267 | } |
1268 | |
1269 | /* |
1270 | * Now enable the port |
1271 | */ |
1272 | if (mpt_send_port_enable(mpt, 0) != MPT_OK) { |
1273 | mpt_prt(mpt, "failed to enable port 0" ); |
1274 | continue; |
1275 | } |
1276 | |
1277 | if (mpt->verbose > 1) { |
1278 | mpt_prt(mpt, "enabled port 0" ); |
1279 | } |
1280 | |
1281 | /* Everything worked */ |
1282 | break; |
1283 | } |
1284 | |
1285 | if (try >= MPT_MAX_TRYS) { |
1286 | mpt_prt(mpt, "failed to initialize IOC" ); |
1287 | return (EIO); |
1288 | } |
1289 | |
1290 | if (mpt->verbose > 1) { |
1291 | mpt_prt(mpt, "enabling interrupts" ); |
1292 | } |
1293 | |
1294 | mpt_enable_ints(mpt); |
1295 | return (0); |
1296 | } |
1297 | |
1298 | /* |
1299 | * Endian Conversion Functions- only used on Big Endian machines |
1300 | */ |
1301 | #if _BYTE_ORDER == _BIG_ENDIAN |
1302 | void |
1303 | mpt2host_sge_simple_union(SGE_SIMPLE_UNION *sge) |
1304 | { |
1305 | |
1306 | MPT_2_HOST32(sge, FlagsLength); |
1307 | MPT_2_HOST32(sge, _u.Address64.Low); |
1308 | MPT_2_HOST32(sge, _u.Address64.High); |
1309 | } |
1310 | |
1311 | void |
1312 | mpt2host_iocfacts_reply(MSG_IOC_FACTS_REPLY *rp) |
1313 | { |
1314 | |
1315 | MPT_2_HOST16(rp, MsgVersion); |
1316 | #if 0 |
1317 | MPT_2_HOST16(rp, HeaderVersion); |
1318 | #endif |
1319 | MPT_2_HOST32(rp, MsgContext); |
1320 | MPT_2_HOST16(rp, IOCExceptions); |
1321 | MPT_2_HOST16(rp, IOCStatus); |
1322 | MPT_2_HOST32(rp, IOCLogInfo); |
1323 | MPT_2_HOST16(rp, ReplyQueueDepth); |
1324 | MPT_2_HOST16(rp, RequestFrameSize); |
1325 | MPT_2_HOST16(rp, Reserved_0101_FWVersion); |
1326 | MPT_2_HOST16(rp, ProductID); |
1327 | MPT_2_HOST32(rp, CurrentHostMfaHighAddr); |
1328 | MPT_2_HOST16(rp, GlobalCredits); |
1329 | MPT_2_HOST32(rp, CurrentSenseBufferHighAddr); |
1330 | MPT_2_HOST16(rp, CurReplyFrameSize); |
1331 | MPT_2_HOST32(rp, FWImageSize); |
1332 | #if 0 |
1333 | MPT_2_HOST32(rp, IOCCapabilities); |
1334 | #endif |
1335 | MPT_2_HOST32(rp, FWVersion.Word); |
1336 | #if 0 |
1337 | MPT_2_HOST16(rp, HighPriorityQueueDepth); |
1338 | MPT_2_HOST16(rp, Reserved2); |
1339 | mpt2host_sge_simple_union(&rp->HostPageBufferSGE); |
1340 | MPT_2_HOST32(rp, ReplyFifoHostSignalingAddr); |
1341 | #endif |
1342 | } |
1343 | |
1344 | void |
1345 | mpt2host_portfacts_reply(MSG_PORT_FACTS_REPLY *pfp) |
1346 | { |
1347 | |
1348 | MPT_2_HOST16(pfp, Reserved); |
1349 | MPT_2_HOST16(pfp, Reserved1); |
1350 | MPT_2_HOST32(pfp, MsgContext); |
1351 | MPT_2_HOST16(pfp, Reserved2); |
1352 | MPT_2_HOST16(pfp, IOCStatus); |
1353 | MPT_2_HOST32(pfp, IOCLogInfo); |
1354 | MPT_2_HOST16(pfp, MaxDevices); |
1355 | MPT_2_HOST16(pfp, PortSCSIID); |
1356 | MPT_2_HOST16(pfp, ProtocolFlags); |
1357 | MPT_2_HOST16(pfp, MaxPostedCmdBuffers); |
1358 | MPT_2_HOST16(pfp, MaxPersistentIDs); |
1359 | MPT_2_HOST16(pfp, MaxLanBuckets); |
1360 | MPT_2_HOST16(pfp, Reserved4); |
1361 | MPT_2_HOST32(pfp, Reserved5); |
1362 | } |
1363 | |
1364 | void |
1365 | mpt2host_config_page_scsi_port_0(fCONFIG_PAGE_SCSI_PORT_0 *sp0) |
1366 | { |
1367 | |
1368 | MPT_2_HOST32(sp0, Capabilities); |
1369 | MPT_2_HOST32(sp0, PhysicalInterface); |
1370 | } |
1371 | |
1372 | void |
1373 | mpt2host_config_page_scsi_port_1(fCONFIG_PAGE_SCSI_PORT_1 *sp1) |
1374 | { |
1375 | |
1376 | MPT_2_HOST32(sp1, Configuration); |
1377 | MPT_2_HOST32(sp1, OnBusTimerValue); |
1378 | #if 0 |
1379 | MPT_2_HOST16(sp1, IDConfig); |
1380 | #endif |
1381 | } |
1382 | |
1383 | void |
1384 | host2mpt_config_page_scsi_port_1(fCONFIG_PAGE_SCSI_PORT_1 *sp1) |
1385 | { |
1386 | |
1387 | HOST_2_MPT32(sp1, Configuration); |
1388 | HOST_2_MPT32(sp1, OnBusTimerValue); |
1389 | #if 0 |
1390 | HOST_2_MPT16(sp1, IDConfig); |
1391 | #endif |
1392 | } |
1393 | |
1394 | void |
1395 | mpt2host_config_page_scsi_port_2(fCONFIG_PAGE_SCSI_PORT_2 *sp2) |
1396 | { |
1397 | int i; |
1398 | |
1399 | MPT_2_HOST32(sp2, PortFlags); |
1400 | MPT_2_HOST32(sp2, PortSettings); |
1401 | for (i = 0; i < sizeof(sp2->DeviceSettings) / |
1402 | sizeof(*sp2->DeviceSettings); i++) { |
1403 | MPT_2_HOST16(sp2, DeviceSettings[i].DeviceFlags); |
1404 | } |
1405 | } |
1406 | |
1407 | void |
1408 | mpt2host_config_page_scsi_device_0(fCONFIG_PAGE_SCSI_DEVICE_0 *sd0) |
1409 | { |
1410 | |
1411 | MPT_2_HOST32(sd0, NegotiatedParameters); |
1412 | MPT_2_HOST32(sd0, Information); |
1413 | } |
1414 | |
1415 | void |
1416 | host2mpt_config_page_scsi_device_0(fCONFIG_PAGE_SCSI_DEVICE_0 *sd0) |
1417 | { |
1418 | |
1419 | HOST_2_MPT32(sd0, NegotiatedParameters); |
1420 | HOST_2_MPT32(sd0, Information); |
1421 | } |
1422 | |
1423 | void |
1424 | mpt2host_config_page_scsi_device_1(fCONFIG_PAGE_SCSI_DEVICE_1 *sd1) |
1425 | { |
1426 | |
1427 | MPT_2_HOST32(sd1, RequestedParameters); |
1428 | MPT_2_HOST32(sd1, Reserved); |
1429 | MPT_2_HOST32(sd1, Configuration); |
1430 | } |
1431 | |
1432 | void |
1433 | host2mpt_config_page_scsi_device_1(fCONFIG_PAGE_SCSI_DEVICE_1 *sd1) |
1434 | { |
1435 | |
1436 | HOST_2_MPT32(sd1, RequestedParameters); |
1437 | HOST_2_MPT32(sd1, Reserved); |
1438 | HOST_2_MPT32(sd1, Configuration); |
1439 | } |
1440 | |
1441 | void |
1442 | mpt2host_config_page_fc_port_0(fCONFIG_PAGE_FC_PORT_0 *fp0) |
1443 | { |
1444 | |
1445 | MPT_2_HOST32(fp0, Flags); |
1446 | MPT_2_HOST32(fp0, PortIdentifier); |
1447 | MPT_2_HOST32(fp0, WWNN.Low); |
1448 | MPT_2_HOST32(fp0, WWNN.High); |
1449 | MPT_2_HOST32(fp0, WWPN.Low); |
1450 | MPT_2_HOST32(fp0, WWPN.High); |
1451 | MPT_2_HOST32(fp0, SupportedServiceClass); |
1452 | MPT_2_HOST32(fp0, SupportedSpeeds); |
1453 | MPT_2_HOST32(fp0, CurrentSpeed); |
1454 | MPT_2_HOST32(fp0, MaxFrameSize); |
1455 | MPT_2_HOST32(fp0, FabricWWNN.Low); |
1456 | MPT_2_HOST32(fp0, FabricWWNN.High); |
1457 | MPT_2_HOST32(fp0, FabricWWPN.Low); |
1458 | MPT_2_HOST32(fp0, FabricWWPN.High); |
1459 | MPT_2_HOST32(fp0, DiscoveredPortsCount); |
1460 | MPT_2_HOST32(fp0, MaxInitiators); |
1461 | } |
1462 | |
1463 | void |
1464 | mpt2host_config_page_fc_port_1(fCONFIG_PAGE_FC_PORT_1 *fp1) |
1465 | { |
1466 | |
1467 | MPT_2_HOST32(fp1, Flags); |
1468 | MPT_2_HOST32(fp1, NoSEEPROMWWNN.Low); |
1469 | MPT_2_HOST32(fp1, NoSEEPROMWWNN.High); |
1470 | MPT_2_HOST32(fp1, NoSEEPROMWWPN.Low); |
1471 | MPT_2_HOST32(fp1, NoSEEPROMWWPN.High); |
1472 | } |
1473 | |
1474 | void |
1475 | host2mpt_config_page_fc_port_1(fCONFIG_PAGE_FC_PORT_1 *fp1) |
1476 | { |
1477 | |
1478 | HOST_2_MPT32(fp1, Flags); |
1479 | HOST_2_MPT32(fp1, NoSEEPROMWWNN.Low); |
1480 | HOST_2_MPT32(fp1, NoSEEPROMWWNN.High); |
1481 | HOST_2_MPT32(fp1, NoSEEPROMWWPN.Low); |
1482 | HOST_2_MPT32(fp1, NoSEEPROMWWPN.High); |
1483 | } |
1484 | |
1485 | void |
1486 | mpt2host_config_page_raid_vol_0(fCONFIG_PAGE_RAID_VOL_0 *volp) |
1487 | { |
1488 | int i; |
1489 | |
1490 | MPT_2_HOST16(volp, VolumeStatus.Reserved); |
1491 | MPT_2_HOST16(volp, VolumeSettings.Settings); |
1492 | MPT_2_HOST32(volp, MaxLBA); |
1493 | #if 0 |
1494 | MPT_2_HOST32(volp, MaxLBAHigh); |
1495 | #endif |
1496 | MPT_2_HOST32(volp, StripeSize); |
1497 | MPT_2_HOST32(volp, Reserved2); |
1498 | MPT_2_HOST32(volp, Reserved3); |
1499 | for (i = 0; i < MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX; i++) { |
1500 | MPT_2_HOST16(volp, PhysDisk[i].Reserved); |
1501 | } |
1502 | } |
1503 | |
1504 | void |
1505 | mpt2host_config_page_raid_phys_disk_0(fCONFIG_PAGE_RAID_PHYS_DISK_0 *rpd0) |
1506 | { |
1507 | |
1508 | MPT_2_HOST32(rpd0, Reserved1); |
1509 | MPT_2_HOST16(rpd0, PhysDiskStatus.Reserved); |
1510 | MPT_2_HOST32(rpd0, MaxLBA); |
1511 | MPT_2_HOST16(rpd0, ErrorData.Reserved); |
1512 | MPT_2_HOST16(rpd0, ErrorData.ErrorCount); |
1513 | MPT_2_HOST16(rpd0, ErrorData.SmartCount); |
1514 | } |
1515 | |
1516 | void |
1517 | mpt2host_config_page_ioc_2(fCONFIG_PAGE_IOC_2 *ioc2) |
1518 | { |
1519 | MPT_2_HOST32(ioc2, CapabilitiesFlags); |
1520 | } |
1521 | |
1522 | #endif |
1523 | |