| Commit | Line | Data |
|---|---|---|
| df54c2f9 SW |
1 | /* |
| 2 | * Copyright (c) 2004-07 Applied Micro Circuits Corporation. | |
| 3 | * Copyright (c) 2004-05 Vinod Kashyap | |
| 4 | * All rights reserved. | |
| 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, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| 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 | |
| 19 | * FOR 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 | * | |
| 4fbf05f9 | 27 | * $FreeBSD: src/sys/dev/twa/tw_cl_misc.c,v 1.7 2010/08/30 19:15:04 delphij Exp $ |
| df54c2f9 SW |
28 | */ |
| 29 | ||
| 30 | /* | |
| 31 | * AMCC'S 3ware driver for 9000 series storage controllers. | |
| 32 | * | |
| 33 | * Author: Vinod Kashyap | |
| 34 | * Modifications by: Adam Radford | |
| 35 | * Modifications by: Manjunath Ranganathaiah | |
| 36 | */ | |
| 37 | ||
| 38 | ||
| 39 | /* | |
| 40 | * Common Layer miscellaneous functions. | |
| 41 | */ | |
| 42 | ||
| 43 | ||
| 44 | #include "tw_osl_share.h" | |
| 45 | #include "tw_cl_share.h" | |
| 46 | #include "tw_cl_fwif.h" | |
| 47 | #include "tw_cl_ioctl.h" | |
| 48 | #include "tw_cl.h" | |
| 49 | #include "tw_cl_externs.h" | |
| 50 | #include "tw_osl_ioctl.h" | |
| 51 | ||
| 52 | ||
| 53 | ||
| 54 | /* AEN severity table. */ | |
| 55 | TW_INT8 *tw_cli_severity_string_table[] = { | |
| 56 | "None", | |
| 57 | TW_CL_SEVERITY_ERROR_STRING, | |
| 58 | TW_CL_SEVERITY_WARNING_STRING, | |
| 59 | TW_CL_SEVERITY_INFO_STRING, | |
| 60 | TW_CL_SEVERITY_DEBUG_STRING, | |
| 61 | "" | |
| 62 | }; | |
| 63 | ||
| 64 | ||
| 65 | ||
| 66 | /* | |
| 67 | * Function name: tw_cli_drain_complete_queue | |
| 68 | * Description: This function gets called during a controller reset. | |
| 69 | * It errors back to the OS Layer, all those requests that | |
| 70 | * are in the complete queue, at the time of the reset. | |
| 71 | * Any CL internal requests will be simply freed. | |
| 72 | * | |
| 73 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 74 | * Output: None | |
| 75 | * Return value: None | |
| 76 | */ | |
| 77 | TW_VOID | |
| 78 | tw_cli_drain_complete_queue(struct tw_cli_ctlr_context *ctlr) | |
| 79 | { | |
| 80 | struct tw_cli_req_context *req; | |
| 81 | struct tw_cl_req_packet *req_pkt; | |
| 82 | ||
| 83 | tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 84 | ||
| 85 | /* Walk the busy queue. */ | |
| 4fbf05f9 SW |
86 | while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_COMPLETE_Q)) != |
| 87 | TW_CL_NULL) { | |
| df54c2f9 SW |
88 | if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) { |
| 89 | /* | |
| 90 | * It's an internal request. Set the appropriate | |
| 91 | * error and call the CL internal callback if there's | |
| 92 | * one. If the request originator is polling for | |
| 93 | * completion, he should be checking req->error to | |
| 94 | * determine that the request did not go through. | |
| 95 | * The request originators are responsible for the | |
| 96 | * clean-up. | |
| 97 | */ | |
| 98 | req->error_code = TW_CL_ERR_REQ_BUS_RESET; | |
| 99 | if (req->tw_cli_callback) | |
| 100 | req->tw_cli_callback(req); | |
| 4fbf05f9 SW |
101 | } else if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) { |
| 102 | /* It's a passthru request. Complete it. */ | |
| 103 | if ((req_pkt = req->orig_req) != TW_CL_NULL) { | |
| df54c2f9 | 104 | req_pkt->status = TW_CL_ERR_REQ_BUS_RESET; |
| 4fbf05f9 SW |
105 | |
| 106 | if (req_pkt->tw_osl_callback) | |
| 107 | req_pkt->tw_osl_callback(req->req_handle); | |
| df54c2f9 SW |
108 | } |
| 109 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 4fbf05f9 SW |
110 | } else { |
| 111 | /* It's an external (SCSI) request. Add it to the reset queue. */ | |
| 112 | tw_osl_untimeout(req->req_handle); | |
| 113 | tw_cli_req_q_insert_tail(req, TW_CLI_RESET_Q); | |
| df54c2f9 | 114 | } |
| 4fbf05f9 | 115 | } /* End of while loop */ |
| df54c2f9 SW |
116 | } |
| 117 | ||
| 118 | ||
| 119 | ||
| 120 | /* | |
| 121 | * Function name: tw_cli_drain_busy_queue | |
| 122 | * Description: This function gets called during a controller reset. | |
| 123 | * It errors back to the OS Layer, all those requests that | |
| 124 | * were pending with the firmware, at the time of the | |
| 125 | * reset. | |
| 126 | * | |
| 127 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 128 | * Output: None | |
| 129 | * Return value: None | |
| 130 | */ | |
| 131 | TW_VOID | |
| 132 | tw_cli_drain_busy_queue(struct tw_cli_ctlr_context *ctlr) | |
| 133 | { | |
| 134 | struct tw_cli_req_context *req; | |
| 135 | struct tw_cl_req_packet *req_pkt; | |
| 136 | ||
| 137 | tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 138 | ||
| 139 | /* Walk the busy queue. */ | |
| 4fbf05f9 SW |
140 | while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_BUSY_Q)) != |
| 141 | TW_CL_NULL) { | |
| df54c2f9 SW |
142 | if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) { |
| 143 | /* | |
| 144 | * It's an internal request. Set the appropriate | |
| 145 | * error and call the CL internal callback if there's | |
| 146 | * one. If the request originator is polling for | |
| 147 | * completion, he should be checking req->error to | |
| 148 | * determine that the request did not go through. | |
| 149 | * The request originators are responsible for the | |
| 150 | * clean-up. | |
| 151 | */ | |
| 152 | req->error_code = TW_CL_ERR_REQ_BUS_RESET; | |
| 153 | if (req->tw_cli_callback) | |
| 154 | req->tw_cli_callback(req); | |
| 4fbf05f9 SW |
155 | } else if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) { |
| 156 | /* It's a passthru request. Complete it. */ | |
| 157 | if ((req_pkt = req->orig_req) != TW_CL_NULL) { | |
| df54c2f9 | 158 | req_pkt->status = TW_CL_ERR_REQ_BUS_RESET; |
| 4fbf05f9 SW |
159 | |
| 160 | if (req_pkt->tw_osl_callback) | |
| 161 | req_pkt->tw_osl_callback(req->req_handle); | |
| df54c2f9 SW |
162 | } |
| 163 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 4fbf05f9 SW |
164 | } else { |
| 165 | /* It's an external (SCSI) request. Add it to the reset queue. */ | |
| 166 | tw_osl_untimeout(req->req_handle); | |
| 167 | tw_cli_req_q_insert_tail(req, TW_CLI_RESET_Q); | |
| df54c2f9 | 168 | } |
| 4fbf05f9 | 169 | } /* End of while loop */ |
| df54c2f9 SW |
170 | } |
| 171 | ||
| 172 | ||
| 173 | ||
| 174 | /* | |
| 175 | * Function name: tw_cli_drain_pending_queue | |
| 176 | * Description: This function gets called during a controller reset. | |
| 177 | * It errors back to the OS Layer, all those requests that | |
| 178 | * were in the pending queue, at the time of the reset. | |
| 179 | * | |
| 180 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 181 | * Output: None | |
| 182 | * Return value: None | |
| 183 | */ | |
| 184 | ||
| 185 | TW_VOID | |
| 186 | tw_cli_drain_pending_queue(struct tw_cli_ctlr_context *ctlr) | |
| 187 | { | |
| 188 | struct tw_cli_req_context *req; | |
| 189 | struct tw_cl_req_packet *req_pkt; | |
| 190 | ||
| 191 | tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 192 | ||
| 193 | /* | |
| 194 | * Pull requests off the pending queue, and complete them. | |
| 195 | */ | |
| 4fbf05f9 SW |
196 | while ((req = tw_cli_req_q_remove_head(ctlr, TW_CLI_PENDING_Q)) != |
| 197 | TW_CL_NULL) { | |
| df54c2f9 SW |
198 | if (req->flags & TW_CLI_REQ_FLAGS_INTERNAL) { |
| 199 | /* | |
| 200 | * It's an internal request. Set the appropriate | |
| 201 | * error and call the CL internal callback if there's | |
| 202 | * one. If the request originator is polling for | |
| 203 | * completion, he should be checking req->error to | |
| 204 | * determine that the request did not go through. | |
| 205 | * The request originators are responsible for the | |
| 206 | * clean-up. | |
| 207 | */ | |
| 208 | req->error_code = TW_CL_ERR_REQ_BUS_RESET; | |
| 209 | if (req->tw_cli_callback) | |
| 210 | req->tw_cli_callback(req); | |
| 4fbf05f9 SW |
211 | } else if (req->flags & TW_CLI_REQ_FLAGS_PASSTHRU) { |
| 212 | /* It's a passthru request. Complete it. */ | |
| 213 | if ((req_pkt = req->orig_req) != TW_CL_NULL) { | |
| df54c2f9 | 214 | req_pkt->status = TW_CL_ERR_REQ_BUS_RESET; |
| 4fbf05f9 SW |
215 | |
| 216 | if (req_pkt->tw_osl_callback) | |
| 217 | req_pkt->tw_osl_callback(req->req_handle); | |
| df54c2f9 SW |
218 | } |
| 219 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 4fbf05f9 SW |
220 | } else { |
| 221 | /* It's an external (SCSI) request. Add it to the reset queue. */ | |
| 222 | tw_osl_untimeout(req->req_handle); | |
| 223 | tw_cli_req_q_insert_tail(req, TW_CLI_RESET_Q); | |
| df54c2f9 | 224 | } |
| 4fbf05f9 | 225 | } /* End of while loop */ |
| df54c2f9 SW |
226 | } |
| 227 | ||
| 228 | ||
| 229 | ||
| 230 | /* | |
| 231 | * Function name: tw_cli_drain_response_queue | |
| 232 | * Description: Drain the controller response queue. | |
| 233 | * | |
| 234 | * Input: ctlr -- ptr to per ctlr structure | |
| 235 | * Output: None | |
| 236 | * Return value: 0 -- success | |
| 237 | * non-zero-- failure | |
| 238 | */ | |
| 239 | TW_INT32 | |
| 240 | tw_cli_drain_response_queue(struct tw_cli_ctlr_context *ctlr) | |
| 241 | { | |
| 242 | TW_UINT32 resp; | |
| 243 | TW_UINT32 status_reg; | |
| 244 | ||
| 245 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 246 | ||
| 247 | for (;;) { | |
| 248 | status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle); | |
| 249 | ||
| df54c2f9 SW |
250 | if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) |
| 251 | return(TW_OSL_ESUCCESS); /* no more response queue entries */ | |
| 252 | ||
| 253 | resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle); | |
| 254 | } | |
| 255 | } | |
| 256 | ||
| 257 | ||
| 258 | ||
| 259 | /* | |
| 260 | * Function name: tw_cli_find_response | |
| 261 | * Description: Find a particular response in the ctlr response queue. | |
| 262 | * | |
| 263 | * Input: ctlr -- ptr to per ctlr structure | |
| 264 | * req_id -- request id of the response to look for | |
| 265 | * Output: None | |
| 266 | * Return value: 0 -- success | |
| 267 | * non-zero-- failure | |
| 268 | */ | |
| 269 | TW_INT32 | |
| 270 | tw_cli_find_response(struct tw_cli_ctlr_context *ctlr, TW_INT32 req_id) | |
| 271 | { | |
| 272 | TW_UINT32 resp; | |
| 273 | TW_INT32 resp_id; | |
| 274 | TW_UINT32 status_reg; | |
| 275 | ||
| 276 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 277 | ||
| 278 | for (;;) { | |
| 279 | status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle); | |
| 280 | ||
| df54c2f9 SW |
281 | if (status_reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) |
| 282 | return(TW_OSL_ENOTTY); /* no more response queue entries */ | |
| 283 | ||
| 284 | if (ctlr->device_id == TW_CL_DEVICE_ID_9K) { | |
| 285 | resp = TW_CLI_READ_RESPONSE_QUEUE(ctlr->ctlr_handle); | |
| 286 | resp_id = GET_RESP_ID(resp); | |
| 287 | } else { | |
| 288 | resp = TW_CLI_READ_LARGE_RESPONSE_QUEUE( | |
| 289 | ctlr->ctlr_handle); | |
| 290 | resp_id = GET_LARGE_RESP_ID(resp); | |
| 291 | } | |
| 292 | if (resp_id == req_id) | |
| 293 | return(TW_OSL_ESUCCESS); /* found the req_id */ | |
| 294 | } | |
| 295 | } | |
| 296 | ||
| 297 | ||
| 298 | ||
| 299 | /* | |
| 300 | * Function name: tw_cli_drain_aen_queue | |
| 301 | * Description: Fetches all un-retrieved AEN's posted by fw. | |
| 302 | * | |
| 303 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 304 | * Output: None | |
| 305 | * Return value: 0 -- success | |
| 306 | * non-zero-- failure | |
| 307 | */ | |
| 308 | TW_INT32 | |
| 309 | tw_cli_drain_aen_queue(struct tw_cli_ctlr_context *ctlr) | |
| 310 | { | |
| 311 | struct tw_cli_req_context *req; | |
| 312 | struct tw_cl_command_header *cmd_hdr; | |
| 313 | TW_TIME end_time; | |
| 314 | TW_UINT16 aen_code; | |
| 315 | TW_INT32 error; | |
| 316 | ||
| 317 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 318 | ||
| 319 | for (;;) { | |
| 320 | if ((req = tw_cli_get_request(ctlr | |
| 321 | )) == TW_CL_NULL) { | |
| 322 | error = TW_OSL_EBUSY; | |
| 323 | break; | |
| 324 | } | |
| 325 | ||
| 326 | req->flags |= TW_CLI_REQ_FLAGS_INTERNAL; | |
| 327 | req->tw_cli_callback = TW_CL_NULL; | |
| 328 | if ((error = tw_cli_send_scsi_cmd(req, | |
| 329 | 0x03 /* REQUEST_SENSE */))) { | |
| 330 | tw_cli_dbg_printf(1, ctlr->ctlr_handle, | |
| 331 | tw_osl_cur_func(), | |
| 332 | "Cannot send command to fetch aen"); | |
| 333 | break; | |
| 334 | } | |
| 335 | ||
| 336 | end_time = tw_osl_get_local_time() + | |
| 337 | TW_CLI_REQUEST_TIMEOUT_PERIOD; | |
| 338 | do { | |
| 339 | if ((error = req->error_code)) | |
| 340 | /* | |
| 341 | * This will take care of completion due to | |
| 342 | * a reset, or a failure in | |
| 343 | * tw_cli_submit_pending_queue. | |
| 344 | */ | |
| 345 | goto out; | |
| 346 | ||
| 347 | tw_cli_process_resp_intr(req->ctlr); | |
| 348 | ||
| 349 | if ((req->state != TW_CLI_REQ_STATE_BUSY) && | |
| 350 | (req->state != TW_CLI_REQ_STATE_PENDING)) | |
| 351 | break; | |
| 352 | } while (tw_osl_get_local_time() <= end_time); | |
| 353 | ||
| 354 | if (req->state != TW_CLI_REQ_STATE_COMPLETE) { | |
| 355 | error = TW_OSL_ETIMEDOUT; | |
| 356 | break; | |
| 357 | } | |
| 358 | ||
| 359 | if ((error = req->cmd_pkt->command.cmd_pkt_9k.status)) { | |
| 360 | cmd_hdr = &req->cmd_pkt->cmd_hdr; | |
| 4fbf05f9 | 361 | #if 0 |
| df54c2f9 SW |
362 | tw_cli_create_ctlr_event(ctlr, |
| 363 | TW_CL_MESSAGE_SOURCE_CONTROLLER_ERROR, | |
| 364 | cmd_hdr); | |
| 4fbf05f9 | 365 | #endif // 0 |
| df54c2f9 SW |
366 | break; |
| 367 | } | |
| 368 | ||
| 369 | aen_code = tw_cli_manage_aen(ctlr, req); | |
| 370 | if (aen_code == TWA_AEN_QUEUE_EMPTY) | |
| 371 | break; | |
| 372 | if (aen_code == TWA_AEN_SYNC_TIME_WITH_HOST) | |
| 373 | continue; | |
| 374 | ||
| 375 | ctlr->internal_req_busy = TW_CL_FALSE; | |
| 376 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 377 | } | |
| 378 | ||
| 379 | out: | |
| 380 | if (req) { | |
| 381 | if (req->data) | |
| 382 | ctlr->internal_req_busy = TW_CL_FALSE; | |
| 383 | tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); | |
| 384 | } | |
| 385 | return(error); | |
| 386 | } | |
| 387 | ||
| 388 | ||
| 389 | ||
| 390 | /* | |
| 391 | * Function name: tw_cli_find_aen | |
| 392 | * Description: Reports whether a given AEN ever occurred. | |
| 393 | * | |
| 394 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 395 | * aen_code-- AEN to look for | |
| 396 | * Output: None | |
| 397 | * Return value: 0 -- success | |
| 398 | * non-zero-- failure | |
| 399 | */ | |
| 400 | TW_INT32 | |
| 401 | tw_cli_find_aen(struct tw_cli_ctlr_context *ctlr, TW_UINT16 aen_code) | |
| 402 | { | |
| 403 | TW_UINT32 last_index; | |
| 404 | TW_INT32 i; | |
| 405 | ||
| 406 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 407 | ||
| 408 | if (ctlr->aen_q_wrapped) | |
| 409 | last_index = ctlr->aen_head; | |
| 410 | else | |
| 411 | last_index = 0; | |
| 412 | ||
| 413 | i = ctlr->aen_head; | |
| 414 | do { | |
| 415 | i = (i + ctlr->max_aens_supported - 1) % | |
| 416 | ctlr->max_aens_supported; | |
| 417 | if (ctlr->aen_queue[i].aen_code == aen_code) | |
| 418 | return(TW_OSL_ESUCCESS); | |
| 419 | } while (i != last_index); | |
| 420 | ||
| 421 | return(TW_OSL_EGENFAILURE); | |
| 422 | } | |
| 423 | ||
| 424 | ||
| 425 | ||
| 426 | /* | |
| 427 | * Function name: tw_cli_poll_status | |
| 428 | * Description: Poll for a given status to show up in the firmware | |
| 429 | * status register. | |
| 430 | * | |
| 431 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 432 | * status -- status to look for | |
| 433 | * timeout -- max # of seconds to wait before giving up | |
| 434 | * Output: None | |
| 435 | * Return value: 0 -- success | |
| 436 | * non-zero-- failure | |
| 437 | */ | |
| 438 | TW_INT32 | |
| 439 | tw_cli_poll_status(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status, | |
| 440 | TW_UINT32 timeout) | |
| 441 | { | |
| 442 | TW_TIME end_time; | |
| 443 | TW_UINT32 status_reg; | |
| 444 | ||
| 445 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 446 | ||
| 447 | end_time = tw_osl_get_local_time() + timeout; | |
| 448 | do { | |
| 449 | status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr->ctlr_handle); | |
| 450 | if ((status_reg & status) == status) | |
| 451 | /* got the required bit(s) */ | |
| 452 | return(TW_OSL_ESUCCESS); | |
| 453 | ||
| 454 | tw_osl_delay(1000); | |
| 455 | } while (tw_osl_get_local_time() <= end_time); | |
| 456 | ||
| 457 | return(TW_OSL_ETIMEDOUT); | |
| 458 | } | |
| 459 | ||
| 460 | ||
| 461 | ||
| 462 | /* | |
| 463 | * Function name: tw_cl_create_event | |
| 464 | * Description: Creates and queues ctlr/CL/OSL AEN's to be | |
| 465 | * supplied to user-space tools on request. | |
| 466 | * Also notifies OS Layer. | |
| 467 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 468 | * queue_event-- TW_CL_TRUE --> queue event; | |
| 469 | * TW_CL_FALSE--> don't queue event | |
| 470 | * (simply notify OSL) | |
| 471 | * event_src -- source of event | |
| 472 | * event_code -- AEN/error code | |
| 473 | * severity -- severity of event | |
| 474 | * severity_str--Text description of severity | |
| 475 | * event_desc -- standard string related to the event/error | |
| 476 | * event_specific_desc -- format string for additional | |
| 477 | * info about the event | |
| 478 | * ... -- additional arguments conforming to the format | |
| 479 | * specified by event_specific_desc | |
| 480 | * Output: None | |
| 481 | * Return value: None | |
| 482 | */ | |
| 483 | TW_VOID | |
| 484 | tw_cl_create_event(struct tw_cl_ctlr_handle *ctlr_handle, | |
| 485 | TW_UINT8 queue_event, TW_UINT8 event_src, TW_UINT16 event_code, | |
| 486 | TW_UINT8 severity, TW_UINT8 *severity_str, TW_UINT8 *event_desc, | |
| 487 | TW_UINT8 *event_specific_desc, ...) | |
| 488 | { | |
| 489 | struct tw_cli_ctlr_context *ctlr = ctlr_handle->cl_ctlr_ctxt; | |
| 490 | struct tw_cl_event_packet event_pkt; | |
| 491 | struct tw_cl_event_packet *event; | |
| 492 | TW_UINT32 aen_head; | |
| 493 | va_list ap; | |
| 494 | ||
| 495 | tw_cli_dbg_printf(8, ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 496 | ||
| 497 | if ((ctlr) && (queue_event)) { | |
| 498 | /* Protect access to ctlr->aen_head. */ | |
| 499 | tw_osl_get_lock(ctlr_handle, ctlr->gen_lock); | |
| 500 | ||
| 501 | aen_head = ctlr->aen_head; | |
| 502 | ctlr->aen_head = (aen_head + 1) % ctlr->max_aens_supported; | |
| 503 | ||
| 504 | /* Queue the event. */ | |
| 505 | event = &(ctlr->aen_queue[aen_head]); | |
| 506 | tw_osl_memzero(event->parameter_data, | |
| 507 | sizeof(event->parameter_data)); | |
| 508 | ||
| 509 | if (event->retrieved == TW_CL_AEN_NOT_RETRIEVED) | |
| 510 | ctlr->aen_q_overflow = TW_CL_TRUE; | |
| 511 | event->sequence_id = ++(ctlr->aen_cur_seq_id); | |
| 512 | if ((aen_head + 1) == ctlr->max_aens_supported) { | |
| 513 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, | |
| 514 | tw_osl_cur_func(), "AEN queue wrapped"); | |
| 515 | ctlr->aen_q_wrapped = TW_CL_TRUE; | |
| 516 | } | |
| 517 | ||
| 518 | /* Free access to ctlr->aen_head. */ | |
| 519 | tw_osl_free_lock(ctlr_handle, ctlr->gen_lock); | |
| 520 | } else { | |
| 521 | event = &event_pkt; | |
| 522 | tw_osl_memzero(event, sizeof(struct tw_cl_event_packet)); | |
| 523 | } | |
| 524 | ||
| 525 | event->event_src = event_src; | |
| 526 | event->time_stamp_sec = (TW_UINT32)tw_osl_get_local_time(); | |
| 527 | event->aen_code = event_code; | |
| 528 | event->severity = severity; | |
| 529 | tw_osl_strcpy(event->severity_str, severity_str); | |
| 530 | event->retrieved = TW_CL_AEN_NOT_RETRIEVED; | |
| 531 | ||
| 532 | va_start(ap, event_specific_desc); | |
| 533 | tw_osl_vsprintf(event->parameter_data, event_specific_desc, ap); | |
| 534 | va_end(ap); | |
| 535 | ||
| 536 | event->parameter_len = | |
| 537 | (TW_UINT8)(tw_osl_strlen(event->parameter_data)); | |
| 538 | tw_osl_strcpy(event->parameter_data + event->parameter_len + 1, | |
| 539 | event_desc); | |
| 540 | event->parameter_len += (1 + tw_osl_strlen(event_desc)); | |
| 541 | ||
| 542 | tw_cli_dbg_printf(4, ctlr_handle, tw_osl_cur_func(), | |
| 543 | "event = %x %x %x %x %x %x %x\n %s", | |
| 544 | event->sequence_id, | |
| 545 | event->time_stamp_sec, | |
| 546 | event->aen_code, | |
| 547 | event->severity, | |
| 548 | event->retrieved, | |
| 549 | event->repeat_count, | |
| 550 | event->parameter_len, | |
| 551 | event->parameter_data); | |
| 552 | ||
| 553 | tw_osl_notify_event(ctlr_handle, event); | |
| 554 | } | |
| 555 | ||
| 556 | ||
| 557 | ||
| 558 | /* | |
| 559 | * Function name: tw_cli_get_request | |
| 560 | * Description: Gets a request pkt from the free queue. | |
| 561 | * | |
| 562 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 563 | * req_pkt -- ptr to OSL built req_pkt, if there's one | |
| 564 | * Output: None | |
| 565 | * Return value: ptr to request pkt -- success | |
| 566 | * TW_CL_NULL -- failure | |
| 567 | */ | |
| 568 | struct tw_cli_req_context * | |
| 569 | tw_cli_get_request(struct tw_cli_ctlr_context *ctlr | |
| 570 | ) | |
| 571 | { | |
| 572 | struct tw_cli_req_context *req; | |
| 573 | ||
| 574 | tw_cli_dbg_printf(4, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 575 | ||
| 576 | { | |
| 577 | /* Get a free request packet. */ | |
| 578 | req = tw_cli_req_q_remove_head(ctlr, TW_CLI_FREE_Q); | |
| 579 | } | |
| 580 | ||
| 581 | /* Initialize some fields to their defaults. */ | |
| 582 | if (req) { | |
| 583 | req->req_handle = TW_CL_NULL; | |
| 584 | req->data = TW_CL_NULL; | |
| 585 | req->length = 0; | |
| 586 | req->data_phys = 0; | |
| 587 | req->state = TW_CLI_REQ_STATE_INIT; /* req being initialized */ | |
| 588 | req->flags = 0; | |
| 589 | req->error_code = 0; | |
| 590 | req->orig_req = TW_CL_NULL; | |
| 591 | req->tw_cli_callback = TW_CL_NULL; | |
| 592 | ||
| 593 | /* | |
| 594 | * Look at the status field in the command packet to see how | |
| 595 | * it completed the last time it was used, and zero out only | |
| 596 | * the portions that might have changed. Note that we don't | |
| 597 | * care to zero out the sglist. | |
| 598 | */ | |
| 599 | if (req->cmd_pkt->command.cmd_pkt_9k.status) | |
| 600 | tw_osl_memzero(req->cmd_pkt, | |
| 601 | sizeof(struct tw_cl_command_header) + | |
| 602 | 28 /* max bytes before sglist */); | |
| 603 | else | |
| 604 | tw_osl_memzero(&(req->cmd_pkt->command), | |
| 605 | 28 /* max bytes before sglist */); | |
| 606 | ||
| 607 | } | |
| 608 | return(req); | |
| 609 | } | |
| 610 | ||
| 611 | ||
| 612 | ||
| 613 | /* | |
| 614 | * Function name: tw_cli_dbg_printf | |
| 615 | * Description: Calls OSL print function if dbg_level is appropriate | |
| 616 | * | |
| 617 | * Input: dbg_level -- Determines whether or not to print | |
| 618 | * ctlr_handle -- controller handle | |
| 619 | * cur_func -- text name of calling function | |
| 620 | * fmt -- format string for the arguments to follow | |
| 621 | * ... -- variable number of arguments, to be printed | |
| 622 | * based on the fmt string | |
| 623 | * Output: None | |
| 624 | * Return value: None | |
| 625 | */ | |
| 626 | TW_VOID | |
| 627 | tw_cli_dbg_printf(TW_UINT8 dbg_level, | |
| 628 | struct tw_cl_ctlr_handle *ctlr_handle, const TW_INT8 *cur_func, | |
| 629 | TW_INT8 *fmt, ...) | |
| 630 | { | |
| 631 | #ifdef TW_OSL_DEBUG | |
| 632 | TW_INT8 print_str[256]; | |
| 633 | va_list ap; | |
| 634 | ||
| 635 | tw_osl_memzero(print_str, 256); | |
| 636 | if (dbg_level <= TW_OSL_DEBUG_LEVEL_FOR_CL) { | |
| 637 | tw_osl_sprintf(print_str, "%s: ", cur_func); | |
| 638 | ||
| 639 | va_start(ap, fmt); | |
| 640 | tw_osl_vsprintf(print_str + tw_osl_strlen(print_str), fmt, ap); | |
| 641 | va_end(ap); | |
| 642 | ||
| 643 | tw_osl_strcpy(print_str + tw_osl_strlen(print_str), "\n"); | |
| 644 | tw_osl_dbg_printf(ctlr_handle, print_str); | |
| 645 | } | |
| 646 | #endif /* TW_OSL_DEBUG */ | |
| 647 | } | |
| 648 | ||
| 649 | ||
| 650 | ||
| 651 | /* | |
| 652 | * Function name: tw_cli_notify_ctlr_info | |
| 653 | * Description: Notify OSL of controller info (fw/BIOS versions, etc.). | |
| 654 | * | |
| 655 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 656 | * Output: None | |
| 657 | * Return value: None | |
| 658 | */ | |
| 659 | TW_VOID | |
| 660 | tw_cli_notify_ctlr_info(struct tw_cli_ctlr_context *ctlr) | |
| 661 | { | |
| 662 | TW_INT8 fw_ver[16]; | |
| 663 | TW_INT8 bios_ver[16]; | |
| 664 | TW_INT8 ctlr_model[16]; | |
| 665 | TW_INT32 error[3]; | |
| 666 | TW_UINT8 num_ports = 0; | |
| 667 | ||
| 668 | tw_cli_dbg_printf(5, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 669 | ||
| 670 | /* Get the port count. */ | |
| 671 | error[0] = tw_cli_get_param(ctlr, TWA_PARAM_CONTROLLER_TABLE, | |
| 672 | TWA_PARAM_CONTROLLER_PORT_COUNT, &num_ports, | |
| 673 | 1, TW_CL_NULL); | |
| 674 | ||
| 675 | /* Get the firmware and BIOS versions. */ | |
| 676 | error[0] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE, | |
| 677 | TWA_PARAM_VERSION_FW, fw_ver, 16, TW_CL_NULL); | |
| 678 | error[1] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE, | |
| 679 | TWA_PARAM_VERSION_BIOS, bios_ver, 16, TW_CL_NULL); | |
| 680 | error[2] = tw_cli_get_param(ctlr, TWA_PARAM_VERSION_TABLE, | |
| 681 | TWA_PARAM_CTLR_MODEL, ctlr_model, 16, TW_CL_NULL); | |
| 682 | ||
| 683 | tw_cl_create_event(ctlr->ctlr_handle, TW_CL_FALSE, | |
| 684 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_ERROR, | |
| 685 | 0x1300, 0x3, TW_CL_SEVERITY_INFO_STRING, | |
| 686 | "Controller details:", | |
| 687 | "Model %.16s, %d ports, Firmware %.16s, BIOS %.16s", | |
| 688 | error[2]?(TW_INT8 *)TW_CL_NULL:ctlr_model, | |
| 689 | num_ports, | |
| 690 | error[0]?(TW_INT8 *)TW_CL_NULL:fw_ver, | |
| 691 | error[1]?(TW_INT8 *)TW_CL_NULL:bios_ver); | |
| 692 | } | |
| 693 | ||
| 694 | ||
| 695 | ||
| 696 | /* | |
| 697 | * Function name: tw_cli_check_ctlr_state | |
| 698 | * Description: Makes sure that the fw status register reports a | |
| 699 | * proper status. | |
| 700 | * | |
| 701 | * Input: ctlr -- ptr to CL internal ctlr context | |
| 702 | * status_reg-- value in the status register | |
| 703 | * Output: None | |
| 704 | * Return value: 0 -- no errors | |
| 705 | * non-zero-- errors | |
| 706 | */ | |
| 707 | TW_INT32 | |
| 708 | tw_cli_check_ctlr_state(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status_reg) | |
| 709 | { | |
| 710 | struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle; | |
| 711 | TW_INT32 error = TW_OSL_ESUCCESS; | |
| 712 | ||
| 713 | tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 714 | ||
| 715 | /* Check if the 'micro-controller ready' bit is not set. */ | |
| 716 | if (!(status_reg & TWA_STATUS_MICROCONTROLLER_READY)) { | |
| 717 | TW_INT8 desc[200]; | |
| 718 | ||
| 719 | tw_osl_memzero(desc, 200); | |
| 720 | if (!(ctlr->reset_phase1_in_progress)) { | |
| 4fbf05f9 | 721 | tw_cl_create_event(ctlr_handle, TW_CL_FALSE, |
| df54c2f9 SW |
722 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, |
| 723 | 0x1301, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 724 | "Missing expected status bit(s)", | |
| 725 | "status reg = 0x%x; Missing bits: %s", | |
| 726 | status_reg, | |
| 727 | tw_cli_describe_bits( | |
| 728 | TWA_STATUS_MICROCONTROLLER_READY, | |
| 729 | desc)); | |
| 730 | error = TW_OSL_EGENFAILURE; | |
| 731 | } | |
| 732 | } | |
| 733 | ||
| 734 | /* Check if any error bits are set. */ | |
| 735 | if ((status_reg & TWA_STATUS_UNEXPECTED_BITS) != 0) { | |
| 736 | TW_INT8 desc[200]; | |
| 737 | ||
| 738 | tw_osl_memzero(desc, 200); | |
| 739 | ||
| 740 | /* Skip queue error msgs during 9650SE/9690SA reset */ | |
| 741 | if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) && | |
| 742 | (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) || | |
| 743 | (!(ctlr->reset_in_progress)) || | |
| 744 | ((status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) == 0)) | |
| 4fbf05f9 | 745 | tw_cl_create_event(ctlr_handle, TW_CL_FALSE, |
| df54c2f9 SW |
746 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, |
| 747 | 0x1302, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 748 | "Unexpected status bit(s)", | |
| 749 | "status reg = 0x%x Unexpected bits: %s", | |
| 750 | status_reg & TWA_STATUS_UNEXPECTED_BITS, | |
| 751 | tw_cli_describe_bits(status_reg & | |
| 752 | TWA_STATUS_UNEXPECTED_BITS, desc)); | |
| 753 | ||
| 754 | if (status_reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT) { | |
| 4fbf05f9 | 755 | tw_cl_create_event(ctlr_handle, TW_CL_FALSE, |
| df54c2f9 SW |
756 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, |
| 757 | 0x1303, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 758 | "PCI parity error: clearing... " | |
| 759 | "Re-seat/move/replace card", | |
| 760 | "status reg = 0x%x %s", | |
| 761 | status_reg, | |
| 762 | tw_cli_describe_bits(status_reg, desc)); | |
| 763 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, | |
| 764 | TWA_CONTROL_CLEAR_PARITY_ERROR); | |
| 765 | ||
| 766 | #ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE | |
| 767 | tw_osl_write_pci_config(ctlr->ctlr_handle, | |
| 768 | TW_CLI_PCI_CONFIG_STATUS_OFFSET, | |
| 769 | TWA_PCI_CONFIG_CLEAR_PARITY_ERROR, 2); | |
| 770 | #endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */ | |
| 771 | ||
| 772 | } | |
| 773 | ||
| 774 | if (status_reg & TWA_STATUS_PCI_ABORT_INTERRUPT) { | |
| 4fbf05f9 | 775 | tw_cl_create_event(ctlr_handle, TW_CL_FALSE, |
| df54c2f9 SW |
776 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, |
| 777 | 0x1304, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 778 | "PCI abort: clearing... ", | |
| 779 | "status reg = 0x%x %s", | |
| 780 | status_reg, | |
| 781 | tw_cli_describe_bits(status_reg, desc)); | |
| 782 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, | |
| 783 | TWA_CONTROL_CLEAR_PCI_ABORT); | |
| 784 | ||
| 785 | #ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE | |
| 786 | tw_osl_write_pci_config(ctlr->ctlr_handle, | |
| 787 | TW_CLI_PCI_CONFIG_STATUS_OFFSET, | |
| 788 | TWA_PCI_CONFIG_CLEAR_PCI_ABORT, 2); | |
| 789 | #endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */ | |
| 790 | ||
| 791 | } | |
| 792 | ||
| 793 | if (status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) { | |
| 794 | /* Skip queue error msgs during 9650SE/9690SA reset */ | |
| 795 | if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) && | |
| 796 | (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) || | |
| 797 | (!(ctlr->reset_in_progress))) | |
| 4fbf05f9 | 798 | tw_cl_create_event(ctlr_handle, TW_CL_FALSE, |
| df54c2f9 SW |
799 | TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, |
| 800 | 0x1305, 0x1, TW_CL_SEVERITY_ERROR_STRING, | |
| 801 | "Controller queue error: clearing... ", | |
| 802 | "status reg = 0x%x %s", | |
| 803 | status_reg, | |
| 804 | tw_cli_describe_bits(status_reg, desc)); | |
| 805 | TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, | |
| 806 | TWA_CONTROL_CLEAR_QUEUE_ERROR); | |
| 807 | } | |
| df54c2f9 SW |
808 | } |
| 809 | return(error); | |
| 810 | } | |
| 811 | ||
| 812 | ||
| 813 | ||
| 814 | /* | |
| 815 | * Function name: tw_cli_describe_bits | |
| 816 | * Description: Given the value of the status register, returns a | |
| 817 | * string describing the meaning of each set bit. | |
| 818 | * | |
| 819 | * Input: reg -- status register value | |
| 820 | * Output: Pointer to a string describing each set bit | |
| 821 | * Return value: Pointer to the string describing each set bit | |
| 822 | */ | |
| 823 | TW_INT8 * | |
| 824 | tw_cli_describe_bits(TW_UINT32 reg, TW_INT8 *str) | |
| 825 | { | |
| 826 | tw_osl_strcpy(str, "["); | |
| 827 | ||
| 828 | if (reg & TWA_STATUS_COMMAND_QUEUE_EMPTY) | |
| 829 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_EMPTY,"); | |
| 830 | if (reg & TWA_STATUS_MICROCONTROLLER_READY) | |
| 831 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "MC_RDY,"); | |
| 832 | if (reg & TWA_STATUS_RESPONSE_QUEUE_EMPTY) | |
| 833 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_Q_EMPTY,"); | |
| 834 | if (reg & TWA_STATUS_COMMAND_QUEUE_FULL) | |
| 835 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_Q_FULL,"); | |
| 836 | if (reg & TWA_STATUS_RESPONSE_INTERRUPT) | |
| 837 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "RESP_INTR,"); | |
| 838 | if (reg & TWA_STATUS_COMMAND_INTERRUPT) | |
| 839 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "CMD_INTR,"); | |
| 840 | if (reg & TWA_STATUS_ATTENTION_INTERRUPT) | |
| 841 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "ATTN_INTR,"); | |
| 842 | if (reg & TWA_STATUS_HOST_INTERRUPT) | |
| 843 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "HOST_INTR,"); | |
| 844 | if (reg & TWA_STATUS_PCI_ABORT_INTERRUPT) | |
| 845 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_ABRT,"); | |
| df54c2f9 SW |
846 | if (reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) |
| 847 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "Q_ERR,"); | |
| 848 | if (reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT) | |
| 849 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "PCI_PERR"); | |
| 850 | ||
| 851 | tw_osl_strcpy(&str[tw_osl_strlen(str)], "]"); | |
| 852 | return(str); | |
| 853 | } | |
| 854 | ||
| 855 | ||
| 856 | ||
| 857 | #ifdef TW_OSL_DEBUG | |
| 858 | ||
| 859 | /* | |
| 860 | * Function name: tw_cl_print_ctlr_stats | |
| 861 | * Description: Prints the current status of the controller. | |
| 862 | * | |
| 863 | * Input: ctlr_handle-- controller handle | |
| 864 | * Output: None | |
| 865 | * Return value: None | |
| 866 | */ | |
| 867 | TW_VOID | |
| 868 | tw_cl_print_ctlr_stats(struct tw_cl_ctlr_handle *ctlr_handle) | |
| 869 | { | |
| 870 | struct tw_cli_ctlr_context *ctlr = | |
| 871 | (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt); | |
| 872 | TW_UINT32 status_reg; | |
| 873 | TW_INT8 desc[200]; | |
| 874 | ||
| 875 | tw_cli_dbg_printf(7, ctlr->ctlr_handle, "", "entered"); | |
| 876 | ||
| 877 | /* Print current controller details. */ | |
| 878 | tw_cli_dbg_printf(0, ctlr_handle, "", "cl_ctlr_ctxt = %p", ctlr); | |
| 879 | ||
| 880 | tw_osl_memzero(desc, 200); | |
| 881 | status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle); | |
| 882 | tw_cli_dbg_printf(0, ctlr_handle, "", "status reg = 0x%x %s", | |
| 883 | status_reg, tw_cli_describe_bits(status_reg, desc)); | |
| 884 | ||
| 885 | tw_cli_dbg_printf(0, ctlr_handle, "", "CLq type current max"); | |
| 886 | tw_cli_dbg_printf(0, ctlr_handle, "", "free %04d %04d", | |
| 887 | ctlr->q_stats[TW_CLI_FREE_Q].cur_len, | |
| 888 | ctlr->q_stats[TW_CLI_FREE_Q].max_len); | |
| 889 | tw_cli_dbg_printf(0, ctlr_handle, "", "busy %04d %04d", | |
| 890 | ctlr->q_stats[TW_CLI_BUSY_Q].cur_len, | |
| 891 | ctlr->q_stats[TW_CLI_BUSY_Q].max_len); | |
| 892 | tw_cli_dbg_printf(0, ctlr_handle, "", "pending %04d %04d", | |
| 893 | ctlr->q_stats[TW_CLI_PENDING_Q].cur_len, | |
| 894 | ctlr->q_stats[TW_CLI_PENDING_Q].max_len); | |
| 895 | tw_cli_dbg_printf(0, ctlr_handle, "", "complete %04d %04d", | |
| 896 | ctlr->q_stats[TW_CLI_COMPLETE_Q].cur_len, | |
| 897 | ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len); | |
| 898 | tw_cli_dbg_printf(0, ctlr_handle, "", "AEN queue head %d tail %d", | |
| 899 | ctlr->aen_head, ctlr->aen_tail); | |
| 900 | } | |
| 901 | ||
| 902 | ||
| 903 | ||
| 904 | /* | |
| 905 | * Function name: tw_cl_reset_stats | |
| 906 | * Description: Resets CL maintained statistics for the controller. | |
| 907 | * | |
| 908 | * Input: ctlr_handle-- controller handle | |
| 909 | * Output: None | |
| 910 | * Return value: None | |
| 911 | */ | |
| 912 | TW_VOID | |
| 913 | tw_cl_reset_stats(struct tw_cl_ctlr_handle *ctlr_handle) | |
| 914 | { | |
| 915 | struct tw_cli_ctlr_context *ctlr = | |
| 916 | (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt); | |
| 917 | ||
| 918 | tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(), "entered"); | |
| 919 | ctlr->q_stats[TW_CLI_FREE_Q].max_len = 0; | |
| 920 | ctlr->q_stats[TW_CLI_BUSY_Q].max_len = 0; | |
| 921 | ctlr->q_stats[TW_CLI_PENDING_Q].max_len = 0; | |
| 922 | ctlr->q_stats[TW_CLI_COMPLETE_Q].max_len = 0; | |
| 923 | } | |
| 924 | ||
| 925 | ||
| 926 | ||
| 927 | /* | |
| 928 | * Function name: tw_cli_print_req_info | |
| 929 | * Description: Prints CL internal details of a given request. | |
| 930 | * | |
| 931 | * Input: req -- ptr to CL internal request context | |
| 932 | * Output: None | |
| 933 | * Return value: None | |
| 934 | */ | |
| 935 | TW_VOID | |
| 936 | tw_cl_print_req_info(struct tw_cl_req_handle *req_handle) | |
| 937 | { | |
| 938 | struct tw_cli_req_context *req = req_handle->cl_req_ctxt; | |
| 939 | struct tw_cli_ctlr_context *ctlr = req->ctlr; | |
| 940 | struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle; | |
| 941 | struct tw_cl_command_packet *cmd_pkt = req->cmd_pkt; | |
| 942 | struct tw_cl_command_9k *cmd9k; | |
| 943 | union tw_cl_command_7k *cmd7k; | |
| 944 | TW_UINT8 *cdb; | |
| 945 | TW_VOID *sgl; | |
| 946 | TW_UINT32 sgl_entries; | |
| 947 | TW_UINT32 i; | |
| 948 | ||
| 949 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 950 | "CL details for request:"); | |
| 951 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 952 | "req_handle = %p, ctlr = %p,\n" | |
| 953 | "cmd_pkt = %p, cmd_pkt_phys = 0x%llx,\n" | |
| 954 | "data = %p, length = 0x%x, data_phys = 0x%llx,\n" | |
| 955 | "state = 0x%x, flags = 0x%x, error = 0x%x,\n" | |
| 956 | "orig_req = %p, callback = %p, req_id = 0x%x,\n" | |
| 957 | "next_req = %p, prev_req = %p", | |
| 958 | req_handle, ctlr, | |
| 959 | cmd_pkt, req->cmd_pkt_phys, | |
| 960 | req->data, req->length, req->data_phys, | |
| 961 | req->state, req->flags, req->error_code, | |
| 962 | req->orig_req, req->tw_cli_callback, req->request_id, | |
| 963 | req->link.next, req->link.prev); | |
| 964 | ||
| 965 | if (req->flags & TW_CLI_REQ_FLAGS_9K) { | |
| 966 | cmd9k = &(cmd_pkt->command.cmd_pkt_9k); | |
| 967 | sgl = cmd9k->sg_list; | |
| 968 | sgl_entries = TW_CL_SWAP16( | |
| 969 | GET_SGL_ENTRIES(cmd9k->lun_h4__sgl_entries)); | |
| 970 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 971 | "9K cmd: opcode = 0x%x, unit = 0x%x, req_id = 0x%x,\n" | |
| 972 | "status = 0x%x, sgl_offset = 0x%x, sgl_entries = 0x%x", | |
| 973 | GET_OPCODE(cmd9k->res__opcode), | |
| 974 | cmd9k->unit, | |
| 975 | TW_CL_SWAP16(GET_REQ_ID(cmd9k->lun_l4__req_id)), | |
| 976 | cmd9k->status, | |
| 977 | cmd9k->sgl_offset, | |
| 978 | sgl_entries); | |
| 979 | ||
| 980 | cdb = (TW_UINT8 *)(cmd9k->cdb); | |
| 981 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 982 | "CDB: %x %x %x %x %x %x %x %x" | |
| 983 | "%x %x %x %x %x %x %x %x", | |
| 984 | cdb[0], cdb[1], cdb[2], cdb[3], | |
| 985 | cdb[4], cdb[5], cdb[6], cdb[7], | |
| 986 | cdb[8], cdb[9], cdb[10], cdb[11], | |
| 987 | cdb[12], cdb[13], cdb[14], cdb[15]); | |
| 988 | } else { | |
| 989 | cmd7k = &(cmd_pkt->command.cmd_pkt_7k); | |
| 990 | sgl = cmd7k->param.sgl; | |
| 991 | sgl_entries = (cmd7k->generic.size - | |
| 992 | GET_SGL_OFF(cmd7k->generic.sgl_off__opcode)) / | |
| 993 | ((ctlr->flags & TW_CL_64BIT_ADDRESSES) ? 3 : 2); | |
| 994 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 995 | "7K cmd: opcode = 0x%x, sgl_offset = 0x%x,\n" | |
| 996 | "size = 0x%x, req_id = 0x%x, unit = 0x%x,\n" | |
| 997 | "status = 0x%x, flags = 0x%x, count = 0x%x", | |
| 998 | GET_OPCODE(cmd7k->generic.sgl_off__opcode), | |
| 999 | GET_SGL_OFF(cmd7k->generic.sgl_off__opcode), | |
| 1000 | cmd7k->generic.size, | |
| 1001 | TW_CL_SWAP16(cmd7k->generic.request_id), | |
| 1002 | GET_UNIT(cmd7k->generic.host_id__unit), | |
| 1003 | cmd7k->generic.status, | |
| 1004 | cmd7k->generic.flags, | |
| 1005 | TW_CL_SWAP16(cmd7k->generic.count)); | |
| 1006 | } | |
| 1007 | ||
| 1008 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), "SG entries:"); | |
| 1009 | ||
| 1010 | if (ctlr->flags & TW_CL_64BIT_ADDRESSES) { | |
| 1011 | struct tw_cl_sg_desc64 *sgl64 = (struct tw_cl_sg_desc64 *)sgl; | |
| 1012 | ||
| 1013 | for (i = 0; i < sgl_entries; i++) { | |
| 1014 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 1015 | "0x%llx 0x%x", | |
| 1016 | sgl64[i].address, sgl64[i].length); | |
| 1017 | } | |
| 1018 | } else { | |
| 1019 | struct tw_cl_sg_desc32 *sgl32 = (struct tw_cl_sg_desc32 *)sgl; | |
| 1020 | ||
| 1021 | for (i = 0; i < sgl_entries; i++) { | |
| 1022 | tw_cli_dbg_printf(0, ctlr_handle, tw_osl_cur_func(), | |
| 1023 | "0x%x 0x%x", | |
| 1024 | sgl32[i].address, sgl32[i].length); | |
| 1025 | } | |
| 1026 | } | |
| 1027 | } | |
| 1028 | ||
| 1029 | #endif /* TW_OSL_DEBUG */ |