blob: 3729062db3177a2fd6c57c992a6d0c9b09bb1fa2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/message/fusion/mptscsih.c
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003 * For use with LSI Logic PCI chip/adapter(s)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
5 *
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04006 * Copyright (c) 1999-2005 LSI Logic Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * (mailto:mpt_linux_developer@lsil.com)
8 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
11/*
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; version 2 of the License.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 NO WARRANTY
22 THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
23 CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
24 LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
25 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
26 solely responsible for determining the appropriateness of using and
27 distributing the Program and assumes all risks associated with its
28 exercise of rights under this Agreement, including but not limited to
29 the risks and costs of program errors, damage to or loss of data,
30 programs or equipment, and unavailability or interruption of operations.
31
32 DISCLAIMER OF LIABILITY
33 NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
34 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
36 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
37 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
38 USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
39 HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
40
41 You should have received a copy of the GNU General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44*/
45/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
46
47#include "linux_compat.h" /* linux-2.6 tweaks */
48#include <linux/module.h>
49#include <linux/kernel.h>
50#include <linux/init.h>
51#include <linux/errno.h>
52#include <linux/kdev_t.h>
53#include <linux/blkdev.h>
54#include <linux/delay.h> /* for mdelay */
55#include <linux/interrupt.h> /* needed for in_interrupt() proto */
56#include <linux/reboot.h> /* notifier code */
57#include <linux/sched.h>
58#include <linux/workqueue.h>
59
60#include <scsi/scsi.h>
61#include <scsi/scsi_cmnd.h>
62#include <scsi/scsi_device.h>
63#include <scsi/scsi_host.h>
64#include <scsi/scsi_tcq.h>
Moore, Eric Deane0fc15b2005-09-15 13:17:14 -060065#include <scsi/scsi_dbg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67#include "mptbase.h"
68#include "mptscsih.h"
69
70/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
71#define my_NAME "Fusion MPT SCSI Host driver"
72#define my_VERSION MPT_LINUX_VERSION_COMMON
73#define MYNAM "mptscsih"
74
75MODULE_AUTHOR(MODULEAUTHOR);
76MODULE_DESCRIPTION(my_NAME);
77MODULE_LICENSE("GPL");
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
80
81typedef struct _BIG_SENSE_BUF {
82 u8 data[MPT_SENSE_BUFFER_ALLOC];
83} BIG_SENSE_BUF;
84
85#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */
86#define MPT_SCANDV_DID_RESET (0x00000001)
87#define MPT_SCANDV_SENSE (0x00000002)
88#define MPT_SCANDV_SOME_ERROR (0x00000004)
89#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
90#define MPT_SCANDV_ISSUE_SENSE (0x00000010)
91#define MPT_SCANDV_FALLBACK (0x00000020)
92
93#define MPT_SCANDV_MAX_RETRIES (10)
94
95#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */
96#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */
Moore, Eric Dean466544d2005-09-14 18:09:10 -060097#define MPT_ICFLAG_EBOS 0x04 /* ReadBuffer Echo buffer has EBOS */
98#define MPT_ICFLAG_PHYS_DISK 0x08 /* Any SCSI IO but do Phys Disk Format */
99#define MPT_ICFLAG_TAGGED_CMD 0x10 /* Do tagged IO */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occurred with this command */
101#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */
102
103typedef struct _internal_cmd {
104 char *data; /* data pointer */
105 dma_addr_t data_dma; /* data dma address */
106 int size; /* transfer size */
107 u8 cmd; /* SCSI Op Code */
108 u8 bus; /* bus number */
109 u8 id; /* SCSI ID (virtual) */
110 u8 lun;
111 u8 flags; /* Bit Field - See above */
112 u8 physDiskNum; /* Phys disk number, -1 else */
113 u8 rsvd2;
114 u8 rsvd;
115} INTERNAL_CMD;
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117/*
118 * Other private/forward protos...
119 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400120int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121static void mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400122int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
124static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
125 SCSIIORequest_t *pReq, int req_idx);
126static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400127static void mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
129static int mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
130static u32 SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc);
131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);
133
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400134int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
135int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
James Bottomleyc92f2222006-03-01 09:02:49 -0600137static void mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, struct scsi_device *sdev);
138static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *vtarget, struct scsi_device *sdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static int mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400140int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700142static void mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400144void mptscsih_remove(struct pci_dev *);
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -0700145void mptscsih_shutdown(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146#ifdef CONFIG_PM
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400147int mptscsih_suspend(struct pci_dev *pdev, pm_message_t state);
148int mptscsih_resume(struct pci_dev *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149#endif
150
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151#define SNS_LEN(scp) sizeof((scp)->sense_buffer)
152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
154/**
155 * mptscsih_add_sge - Place a simple SGE at address pAddr.
156 * @pAddr: virtual address for SGE
157 * @flagslength: SGE flags and data transfer length
158 * @dma_addr: Physical address
159 *
160 * This routine places a MPT request frame back on the MPT adapter's
161 * FreeQ.
162 */
163static inline void
164mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
165{
166 if (sizeof(dma_addr_t) == sizeof(u64)) {
167 SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
168 u32 tmp = dma_addr & 0xFFFFFFFF;
169
170 pSge->FlagsLength = cpu_to_le32(flagslength);
171 pSge->Address.Low = cpu_to_le32(tmp);
172 tmp = (u32) ((u64)dma_addr >> 32);
173 pSge->Address.High = cpu_to_le32(tmp);
174
175 } else {
176 SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
177 pSge->FlagsLength = cpu_to_le32(flagslength);
178 pSge->Address = cpu_to_le32(dma_addr);
179 }
180} /* mptscsih_add_sge() */
181
182/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
183/**
184 * mptscsih_add_chain - Place a chain SGE at address pAddr.
185 * @pAddr: virtual address for SGE
186 * @next: nextChainOffset value (u32's)
187 * @length: length of next SGL segment
188 * @dma_addr: Physical address
189 *
190 * This routine places a MPT request frame back on the MPT adapter's
191 * FreeQ.
192 */
193static inline void
194mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
195{
196 if (sizeof(dma_addr_t) == sizeof(u64)) {
197 SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
198 u32 tmp = dma_addr & 0xFFFFFFFF;
199
200 pChain->Length = cpu_to_le16(length);
201 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
202
203 pChain->NextChainOffset = next;
204
205 pChain->Address.Low = cpu_to_le32(tmp);
206 tmp = (u32) ((u64)dma_addr >> 32);
207 pChain->Address.High = cpu_to_le32(tmp);
208 } else {
209 SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
210 pChain->Length = cpu_to_le16(length);
211 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
212 pChain->NextChainOffset = next;
213 pChain->Address = cpu_to_le32(dma_addr);
214 }
215} /* mptscsih_add_chain() */
216
217/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
218/*
219 * mptscsih_getFreeChainBuffer - Function to get a free chain
220 * from the MPT_SCSI_HOST FreeChainQ.
221 * @ioc: Pointer to MPT_ADAPTER structure
222 * @req_idx: Index of the SCSI IO request frame. (output)
223 *
224 * return SUCCESS or FAILED
225 */
226static inline int
227mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex)
228{
229 MPT_FRAME_HDR *chainBuf;
230 unsigned long flags;
231 int rc;
232 int chain_idx;
233
234 dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer called\n",
235 ioc->name));
236 spin_lock_irqsave(&ioc->FreeQlock, flags);
237 if (!list_empty(&ioc->FreeChainQ)) {
238 int offset;
239
240 chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR,
241 u.frame.linkage.list);
242 list_del(&chainBuf->u.frame.linkage.list);
243 offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer;
244 chain_idx = offset / ioc->req_sz;
245 rc = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200246 dsgprintk((MYIOC_s_ERR_FMT "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n",
247 ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 } else {
249 rc = FAILED;
250 chain_idx = MPT_HOST_NO_CHAIN;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200251 dfailprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer failed\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 ioc->name));
253 }
254 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
255
256 *retIndex = chain_idx;
257 return rc;
258} /* mptscsih_getFreeChainBuffer() */
259
260/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
261/*
262 * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
263 * SCSIIORequest_t Message Frame.
264 * @ioc: Pointer to MPT_ADAPTER structure
265 * @SCpnt: Pointer to scsi_cmnd structure
266 * @pReq: Pointer to SCSIIORequest_t structure
267 *
268 * Returns ...
269 */
270static int
271mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
272 SCSIIORequest_t *pReq, int req_idx)
273{
274 char *psge;
275 char *chainSge;
276 struct scatterlist *sg;
277 int frm_sz;
278 int sges_left, sg_done;
279 int chain_idx = MPT_HOST_NO_CHAIN;
280 int sgeOffset;
281 int numSgeSlots, numSgeThisFrame;
282 u32 sgflags, sgdir, thisxfer = 0;
283 int chain_dma_off = 0;
284 int newIndex;
285 int ii;
286 dma_addr_t v2;
287 u32 RequestNB;
288
289 sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
290 if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
291 sgdir = MPT_TRANSFER_HOST_TO_IOC;
292 } else {
293 sgdir = MPT_TRANSFER_IOC_TO_HOST;
294 }
295
296 psge = (char *) &pReq->SGL;
297 frm_sz = ioc->req_sz;
298
299 /* Map the data portion, if any.
300 * sges_left = 0 if no data transfer.
301 */
302 if ( (sges_left = SCpnt->use_sg) ) {
303 sges_left = pci_map_sg(ioc->pcidev,
304 (struct scatterlist *) SCpnt->request_buffer,
305 SCpnt->use_sg,
306 SCpnt->sc_data_direction);
307 if (sges_left == 0)
308 return FAILED;
309 } else if (SCpnt->request_bufflen) {
310 SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev,
311 SCpnt->request_buffer,
312 SCpnt->request_bufflen,
313 SCpnt->sc_data_direction);
314 dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
315 ioc->name, SCpnt, SCpnt->request_bufflen));
316 mptscsih_add_sge((char *) &pReq->SGL,
317 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
318 SCpnt->SCp.dma_handle);
319
320 return SUCCESS;
321 }
322
323 /* Handle the SG case.
324 */
325 sg = (struct scatterlist *) SCpnt->request_buffer;
326 sg_done = 0;
327 sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
328 chainSge = NULL;
329
330 /* Prior to entering this loop - the following must be set
331 * current MF: sgeOffset (bytes)
332 * chainSge (Null if original MF is not a chain buffer)
333 * sg_done (num SGE done for this MF)
334 */
335
336nextSGEset:
337 numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
338 numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
339
340 sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
341
342 /* Get first (num - 1) SG elements
343 * Skip any SG entries with a length of 0
344 * NOTE: at finish, sg and psge pointed to NEXT data/location positions
345 */
346 for (ii=0; ii < (numSgeThisFrame-1); ii++) {
347 thisxfer = sg_dma_len(sg);
348 if (thisxfer == 0) {
349 sg ++; /* Get next SG element from the OS */
350 sg_done++;
351 continue;
352 }
353
354 v2 = sg_dma_address(sg);
355 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
356
357 sg++; /* Get next SG element from the OS */
358 psge += (sizeof(u32) + sizeof(dma_addr_t));
359 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
360 sg_done++;
361 }
362
363 if (numSgeThisFrame == sges_left) {
364 /* Add last element, end of buffer and end of list flags.
365 */
366 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
367 MPT_SGE_FLAGS_END_OF_BUFFER |
368 MPT_SGE_FLAGS_END_OF_LIST;
369
370 /* Add last SGE and set termination flags.
371 * Note: Last SGE may have a length of 0 - which should be ok.
372 */
373 thisxfer = sg_dma_len(sg);
374
375 v2 = sg_dma_address(sg);
376 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
377 /*
378 sg++;
379 psge += (sizeof(u32) + sizeof(dma_addr_t));
380 */
381 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
382 sg_done++;
383
384 if (chainSge) {
385 /* The current buffer is a chain buffer,
386 * but there is not another one.
387 * Update the chain element
388 * Offset and Length fields.
389 */
390 mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
391 } else {
392 /* The current buffer is the original MF
393 * and there is no Chain buffer.
394 */
395 pReq->ChainOffset = 0;
396 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200397 dsgprintk((MYIOC_s_INFO_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
399 ioc->RequestNB[req_idx] = RequestNB;
400 }
401 } else {
402 /* At least one chain buffer is needed.
403 * Complete the first MF
404 * - last SGE element, set the LastElement bit
405 * - set ChainOffset (words) for orig MF
406 * (OR finish previous MF chain buffer)
407 * - update MFStructPtr ChainIndex
408 * - Populate chain element
409 * Also
410 * Loop until done.
411 */
412
413 dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
414 ioc->name, sg_done));
415
416 /* Set LAST_ELEMENT flag for last non-chain element
417 * in the buffer. Since psge points at the NEXT
418 * SGE element, go back one SGE element, update the flags
419 * and reset the pointer. (Note: sgflags & thisxfer are already
420 * set properly).
421 */
422 if (sg_done) {
423 u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
424 sgflags = le32_to_cpu(*ptmp);
425 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
426 *ptmp = cpu_to_le32(sgflags);
427 }
428
429 if (chainSge) {
430 /* The current buffer is a chain buffer.
431 * chainSge points to the previous Chain Element.
432 * Update its chain element Offset and Length (must
433 * include chain element size) fields.
434 * Old chain element is now complete.
435 */
436 u8 nextChain = (u8) (sgeOffset >> 2);
437 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
438 mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
439 } else {
440 /* The original MF buffer requires a chain buffer -
441 * set the offset.
442 * Last element in this MF is a chain element.
443 */
444 pReq->ChainOffset = (u8) (sgeOffset >> 2);
445 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
446 dsgprintk((MYIOC_s_ERR_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
447 ioc->RequestNB[req_idx] = RequestNB;
448 }
449
450 sges_left -= sg_done;
451
452
453 /* NOTE: psge points to the beginning of the chain element
454 * in current buffer. Get a chain buffer.
455 */
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200456 if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) {
457 dfailprintk((MYIOC_s_INFO_FMT
458 "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n",
459 ioc->name, pReq->CDB[0], SCpnt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 return FAILED;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200461 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462
463 /* Update the tracking arrays.
464 * If chainSge == NULL, update ReqToChain, else ChainToChain
465 */
466 if (chainSge) {
467 ioc->ChainToChain[chain_idx] = newIndex;
468 } else {
469 ioc->ReqToChain[req_idx] = newIndex;
470 }
471 chain_idx = newIndex;
472 chain_dma_off = ioc->req_sz * chain_idx;
473
474 /* Populate the chainSGE for the current buffer.
475 * - Set chain buffer pointer to psge and fill
476 * out the Address and Flags fields.
477 */
478 chainSge = (char *) psge;
479 dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
480 psge, req_idx));
481
482 /* Start the SGE for the next buffer
483 */
484 psge = (char *) (ioc->ChainBuffer + chain_dma_off);
485 sgeOffset = 0;
486 sg_done = 0;
487
488 dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
489 psge, chain_idx));
490
491 /* Start the SGE for the next buffer
492 */
493
494 goto nextSGEset;
495 }
496
497 return SUCCESS;
498} /* mptscsih_AddSGE() */
499
500/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
501/*
502 * mptscsih_io_done - Main SCSI IO callback routine registered to
503 * Fusion MPT (base) driver
504 * @ioc: Pointer to MPT_ADAPTER structure
505 * @mf: Pointer to original MPT request frame
506 * @r: Pointer to MPT reply frame (NULL if TurboReply)
507 *
508 * This routine is called from mpt.c::mpt_interrupt() at the completion
509 * of any SCSI IO request.
510 * This routine is registered with the Fusion MPT (base) driver at driver
511 * load/init time via the mpt_register() API call.
512 *
513 * Returns 1 indicating alloc'd request frame ptr should be freed.
514 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400515int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
517{
518 struct scsi_cmnd *sc;
519 MPT_SCSI_HOST *hd;
520 SCSIIORequest_t *pScsiReq;
521 SCSIIOReply_t *pScsiReply;
Moore, Eric2254c862006-01-17 17:06:29 -0700522 u16 req_idx, req_idx_MR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
524 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
525
526 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
Moore, Eric2254c862006-01-17 17:06:29 -0700527 req_idx_MR = (mr != NULL) ?
528 le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx) : req_idx;
529 if ((req_idx != req_idx_MR) ||
530 (mf->u.frame.linkage.arg1 == 0xdeadbeaf)) {
531 printk(MYIOC_s_ERR_FMT "Received a mf that was already freed\n",
532 ioc->name);
533 printk (MYIOC_s_ERR_FMT
534 "req_idx=%x req_idx_MR=%x mf=%p mr=%p sc=%p\n",
535 ioc->name, req_idx, req_idx_MR, mf, mr,
536 hd->ScsiLookup[req_idx_MR]);
537 return 0;
538 }
539
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 sc = hd->ScsiLookup[req_idx];
541 if (sc == NULL) {
542 MPIHeader_t *hdr = (MPIHeader_t *)mf;
543
544 /* Remark: writeSDP1 will use the ScsiDoneCtx
545 * If a SCSI I/O cmd, device disabled by OS and
546 * completion done. Cannot touch sc struct. Just free mem.
547 */
548 if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
549 printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n",
550 ioc->name);
551
552 mptscsih_freeChainBuffers(ioc, req_idx);
553 return 1;
554 }
555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 sc->result = DID_OK << 16; /* Set default reply as OK */
557 pScsiReq = (SCSIIORequest_t *) mf;
558 pScsiReply = (SCSIIOReply_t *) mr;
559
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200560 if((ioc->facts.MsgVersion >= MPI_VERSION_01_05) && pScsiReply){
561 dmfprintk((MYIOC_s_INFO_FMT
562 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d,task-tag=%d)\n",
563 ioc->name, mf, mr, sc, req_idx, pScsiReply->TaskTag));
564 }else{
565 dmfprintk((MYIOC_s_INFO_FMT
566 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n",
567 ioc->name, mf, mr, sc, req_idx));
568 }
569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 if (pScsiReply == NULL) {
571 /* special context reply handling */
572 ;
573 } else {
574 u32 xfer_cnt;
575 u16 status;
576 u8 scsi_state, scsi_status;
577
578 status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
579 scsi_state = pScsiReply->SCSIState;
580 scsi_status = pScsiReply->SCSIStatus;
581 xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
582 sc->resid = sc->request_bufflen - xfer_cnt;
583
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600584 /*
585 * if we get a data underrun indication, yet no data was
586 * transferred and the SCSI status indicates that the
587 * command was never started, change the data underrun
588 * to success
589 */
590 if (status == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
591 (scsi_status == MPI_SCSI_STATUS_BUSY ||
592 scsi_status == MPI_SCSI_STATUS_RESERVATION_CONFLICT ||
593 scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)) {
594 status = MPI_IOCSTATUS_SUCCESS;
595 }
596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n"
598 "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n"
599 "resid=%d bufflen=%d xfer_cnt=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700600 ioc->id, sc->device->id, sc->device->lun,
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600601 status, scsi_state, scsi_status, sc->resid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 sc->request_bufflen, xfer_cnt));
603
604 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400605 mptscsih_copy_sense_data(sc, hd, mf, pScsiReply);
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 /*
608 * Look for + dump FCP ResponseInfo[]!
609 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600610 if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID &&
611 pScsiReply->ResponseInfo) {
612 printk(KERN_NOTICE "ha=%d id=%d lun=%d: "
613 "FCP_ResponseInfo=%08xh\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700614 ioc->id, sc->device->id, sc->device->lun,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 le32_to_cpu(pScsiReply->ResponseInfo));
616 }
617
618 switch(status) {
619 case MPI_IOCSTATUS_BUSY: /* 0x0002 */
620 /* CHECKME!
621 * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
622 * But not: DID_BUS_BUSY lest one risk
623 * killing interrupt handler:-(
624 */
625 sc->result = SAM_STAT_BUSY;
626 break;
627
628 case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
629 case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
630 sc->result = DID_BAD_TARGET << 16;
631 break;
632
633 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
634 /* Spoof to SCSI Selection Timeout! */
635 sc->result = DID_NO_CONNECT << 16;
636
637 if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF)
638 hd->sel_timeout[pScsiReq->TargetID]++;
639 break;
640
641 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
642 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
643 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
644 /* Linux handles an unsolicited DID_RESET better
645 * than an unsolicited DID_ABORT.
646 */
647 sc->result = DID_RESET << 16;
648
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 break;
650
651 case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600652 sc->resid = sc->request_bufflen - xfer_cnt;
653 if((xfer_cnt==0)||(sc->underflow > xfer_cnt))
654 sc->result=DID_SOFT_ERROR << 16;
655 else /* Sufficient data transfer occurred */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 sc->result = (DID_OK << 16) | scsi_status;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600657 dreplyprintk((KERN_NOTICE
658 "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->device->id));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 break;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400660
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
662 /*
663 * Do upfront check for valid SenseData and give it
664 * precedence!
665 */
666 sc->result = (DID_OK << 16) | scsi_status;
667 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
668 /* Have already saved the status and sense data
669 */
670 ;
671 } else {
672 if (xfer_cnt < sc->underflow) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600673 if (scsi_status == SAM_STAT_BUSY)
674 sc->result = SAM_STAT_BUSY;
675 else
676 sc->result = DID_SOFT_ERROR << 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 }
678 if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
679 /* What to do?
680 */
681 sc->result = DID_SOFT_ERROR << 16;
682 }
683 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
684 /* Not real sure here either... */
685 sc->result = DID_RESET << 16;
686 }
687 }
688
689 dreplyprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n",
690 sc->underflow));
691 dreplyprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt));
692 /* Report Queue Full
693 */
694 if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)
695 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400696
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 break;
698
Moore, Eric7e551472006-01-16 18:53:21 -0700699 case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
700 sc->resid=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
702 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600703 if (scsi_status == MPI_SCSI_STATUS_BUSY)
704 sc->result = (DID_BUS_BUSY << 16) | scsi_status;
705 else
706 sc->result = (DID_OK << 16) | scsi_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 if (scsi_state == 0) {
708 ;
709 } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
710 /*
711 * If running against circa 200003dd 909 MPT f/w,
712 * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
713 * (QUEUE_FULL) returned from device! --> get 0x0000?128
714 * and with SenseBytes set to 0.
715 */
716 if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
717 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
718
719 }
720 else if (scsi_state &
721 (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
722 ) {
723 /*
724 * What to do?
725 */
726 sc->result = DID_SOFT_ERROR << 16;
727 }
728 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
729 /* Not real sure here either... */
730 sc->result = DID_RESET << 16;
731 }
732 else if (scsi_state & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) {
733 /* Device Inq. data indicates that it supports
734 * QTags, but rejects QTag messages.
735 * This command completed OK.
736 *
737 * Not real sure here either so do nothing... */
738 }
739
740 if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
741 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
742
743 /* Add handling of:
744 * Reservation Conflict, Busy,
745 * Command Terminated, CHECK
746 */
747 break;
748
749 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
750 sc->result = DID_SOFT_ERROR << 16;
751 break;
752
753 case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
754 case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
755 case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
756 case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
757 case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
758 case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
759 case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
761 case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
762 default:
763 /*
764 * What to do?
765 */
766 sc->result = DID_SOFT_ERROR << 16;
767 break;
768
769 } /* switch(status) */
770
771 dreplyprintk((KERN_NOTICE " sc->result is %08xh\n", sc->result));
772 } /* end of address reply case */
773
774 /* Unmap the DMA buffers, if any. */
775 if (sc->use_sg) {
776 pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
777 sc->use_sg, sc->sc_data_direction);
778 } else if (sc->request_bufflen) {
779 pci_unmap_single(ioc->pcidev, sc->SCp.dma_handle,
780 sc->request_bufflen, sc->sc_data_direction);
781 }
782
783 hd->ScsiLookup[req_idx] = NULL;
784
785 sc->scsi_done(sc); /* Issue the command callback */
786
787 /* Free Chain buffers */
788 mptscsih_freeChainBuffers(ioc, req_idx);
789 return 1;
790}
791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792/*
793 * mptscsih_flush_running_cmds - For each command found, search
794 * Scsi_Host instance taskQ and reply to OS.
795 * Called only if recovering from a FW reload.
796 * @hd: Pointer to a SCSI HOST structure
797 *
798 * Returns: None.
799 *
800 * Must be called while new I/Os are being queued.
801 */
802static void
803mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
804{
805 MPT_ADAPTER *ioc = hd->ioc;
806 struct scsi_cmnd *SCpnt;
807 MPT_FRAME_HDR *mf;
808 int ii;
809 int max = ioc->req_depth;
810
811 dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n"));
812 for (ii= 0; ii < max; ii++) {
813 if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
814
815 /* Command found.
816 */
817
818 /* Null ScsiLookup index
819 */
820 hd->ScsiLookup[ii] = NULL;
821
822 mf = MPT_INDEX_2_MFPTR(ioc, ii);
823 dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n",
824 mf, SCpnt));
825
826 /* Set status, free OS resources (SG DMA buffers)
827 * Do OS callback
828 * Free driver resources (chain, msg buffers)
829 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400830 if (SCpnt->use_sg) {
831 pci_unmap_sg(ioc->pcidev,
832 (struct scatterlist *) SCpnt->request_buffer,
833 SCpnt->use_sg,
834 SCpnt->sc_data_direction);
835 } else if (SCpnt->request_bufflen) {
836 pci_unmap_single(ioc->pcidev,
837 SCpnt->SCp.dma_handle,
838 SCpnt->request_bufflen,
839 SCpnt->sc_data_direction);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 }
841 SCpnt->result = DID_RESET << 16;
842 SCpnt->host_scribble = NULL;
843
844 /* Free Chain buffers */
845 mptscsih_freeChainBuffers(ioc, ii);
846
847 /* Free Message frames */
848 mpt_free_msg_frame(ioc, mf);
849
850 SCpnt->scsi_done(SCpnt); /* Issue the command callback */
851 }
852 }
853
854 return;
855}
856
857/*
858 * mptscsih_search_running_cmds - Delete any commands associated
859 * with the specified target and lun. Function called only
860 * when a lun is disable by mid-layer.
861 * Do NOT access the referenced scsi_cmnd structure or
862 * members. Will cause either a paging or NULL ptr error.
Michael Reed05e8ec12006-01-13 14:31:54 -0600863 * (BUT, BUT, BUT, the code does reference it! - mdr)
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700864 * @hd: Pointer to a SCSI HOST structure
865 * @vdevice: per device private data
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 *
867 * Returns: None.
868 *
869 * Called from slave_destroy.
870 */
871static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700872mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873{
874 SCSIIORequest_t *mf = NULL;
875 int ii;
876 int max = hd->ioc->req_depth;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600877 struct scsi_cmnd *sc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700880 vdevice->target_id, vdevice->lun, max));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
882 for (ii=0; ii < max; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600883 if ((sc = hd->ScsiLookup[ii]) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(hd->ioc, ii);
886
887 dsprintk(( "search_running: found (sc=%p, mf = %p) target %d, lun %d \n",
888 hd->ScsiLookup[ii], mf, mf->TargetID, mf->LUN[1]));
889
Moore, Eric914c2d82006-03-14 09:19:36 -0700890 if ((mf->TargetID != ((u8)vdevice->vtarget->target_id)) || (mf->LUN[1] != ((u8) vdevice->lun)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 continue;
892
893 /* Cleanup
894 */
895 hd->ScsiLookup[ii] = NULL;
896 mptscsih_freeChainBuffers(hd->ioc, ii);
897 mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf);
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600898 if (sc->use_sg) {
899 pci_unmap_sg(hd->ioc->pcidev,
900 (struct scatterlist *) sc->request_buffer,
901 sc->use_sg,
902 sc->sc_data_direction);
903 } else if (sc->request_bufflen) {
904 pci_unmap_single(hd->ioc->pcidev,
905 sc->SCp.dma_handle,
906 sc->request_bufflen,
907 sc->sc_data_direction);
908 }
909 sc->host_scribble = NULL;
910 sc->result = DID_NO_CONNECT << 16;
911 sc->scsi_done(sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 }
913 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 return;
915}
916
917/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
919/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
920/*
921 * mptscsih_report_queue_full - Report QUEUE_FULL status returned
922 * from a SCSI target device.
923 * @sc: Pointer to scsi_cmnd structure
924 * @pScsiReply: Pointer to SCSIIOReply_t
925 * @pScsiReq: Pointer to original SCSI request
926 *
927 * This routine periodically reports QUEUE_FULL status returned from a
928 * SCSI target device. It reports this to the console via kernel
929 * printk() API call, not more than once every 10 seconds.
930 */
931static void
932mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
933{
934 long time = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 MPT_SCSI_HOST *hd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400937 if (sc->device == NULL)
938 return;
939 if (sc->device->host == NULL)
940 return;
941 if ((hd = (MPT_SCSI_HOST *)sc->device->host->hostdata) == NULL)
942 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400944 if (time - hd->last_queue_full > 10 * HZ) {
945 dprintk((MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n",
946 hd->ioc->name, 0, sc->device->id, sc->device->lun));
947 hd->last_queue_full = time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949}
950
951/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
952/*
953 * mptscsih_remove - Removed scsi devices
954 * @pdev: Pointer to pci_dev structure
955 *
956 *
957 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400958void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959mptscsih_remove(struct pci_dev *pdev)
960{
961 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
962 struct Scsi_Host *host = ioc->sh;
963 MPT_SCSI_HOST *hd;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400964 int sz1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600966 if(!host) {
967 mpt_detach(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 return;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600969 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 scsi_remove_host(host);
972
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400973 if((hd = (MPT_SCSI_HOST *)host->hostdata) == NULL)
974 return;
975
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -0700976 mptscsih_shutdown(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400978 sz1=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400980 if (hd->ScsiLookup != NULL) {
981 sz1 = hd->ioc->req_depth * sizeof(void *);
982 kfree(hd->ScsiLookup);
983 hd->ScsiLookup = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 }
985
Moore, Eric Dean d485eb82005-05-11 17:37:26 -0600986 /*
987 * Free pointer array.
988 */
989 kfree(hd->Targets);
990 hd->Targets = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400992 dprintk((MYIOC_s_INFO_FMT
993 "Free'd ScsiLookup (%d) memory\n",
994 hd->ioc->name, sz1));
995
Moore, Eric Dean d485eb82005-05-11 17:37:26 -0600996 kfree(hd->info_kbuf);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400997
998 /* NULL the Scsi_Host pointer
999 */
1000 hd->ioc->sh = NULL;
1001
1002 scsi_host_put(host);
1003
1004 mpt_detach(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006}
1007
1008/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1009/*
1010 * mptscsih_shutdown - reboot notifier
1011 *
1012 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001013void
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001014mptscsih_shutdown(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001016 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 struct Scsi_Host *host = ioc->sh;
1018 MPT_SCSI_HOST *hd;
1019
1020 if(!host)
1021 return;
1022
1023 hd = (MPT_SCSI_HOST *)host->hostdata;
1024
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025}
1026
1027#ifdef CONFIG_PM
1028/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1029/*
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001030 * mptscsih_suspend - Fusion MPT scsi driver suspend routine.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 *
1032 *
1033 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001034int
Pavel Machek8d189f72005-04-16 15:25:28 -07001035mptscsih_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001037 mptscsih_shutdown(pdev);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001038 return mpt_suspend(pdev,state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039}
1040
1041/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1042/*
1043 * mptscsih_resume - Fusion MPT scsi driver resume routine.
1044 *
1045 *
1046 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001047int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048mptscsih_resume(struct pci_dev *pdev)
1049{
1050 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
1051 struct Scsi_Host *host = ioc->sh;
1052 MPT_SCSI_HOST *hd;
1053
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001054 mpt_resume(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001055
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 if(!host)
1057 return 0;
1058
1059 hd = (MPT_SCSI_HOST *)host->hostdata;
1060 if(!hd)
1061 return 0;
1062
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 return 0;
1064}
1065
1066#endif
1067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1069/**
1070 * mptscsih_info - Return information about MPT adapter
1071 * @SChost: Pointer to Scsi_Host structure
1072 *
1073 * (linux scsi_host_template.info routine)
1074 *
1075 * Returns pointer to buffer where information was written.
1076 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001077const char *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078mptscsih_info(struct Scsi_Host *SChost)
1079{
1080 MPT_SCSI_HOST *h;
1081 int size = 0;
1082
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 h = (MPT_SCSI_HOST *)SChost->hostdata;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001084
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 if (h) {
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001086 if (h->info_kbuf == NULL)
1087 if ((h->info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
1088 return h->info_kbuf;
1089 h->info_kbuf[0] = '\0';
1090
1091 mpt_print_ioc_summary(h->ioc, h->info_kbuf, &size, 0, 0);
1092 h->info_kbuf[size-1] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 }
1094
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001095 return h->info_kbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096}
1097
1098struct info_str {
1099 char *buffer;
1100 int length;
1101 int offset;
1102 int pos;
1103};
1104
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001105static void
1106mptscsih_copy_mem_info(struct info_str *info, char *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107{
1108 if (info->pos + len > info->length)
1109 len = info->length - info->pos;
1110
1111 if (info->pos + len < info->offset) {
1112 info->pos += len;
1113 return;
1114 }
1115
1116 if (info->pos < info->offset) {
1117 data += (info->offset - info->pos);
1118 len -= (info->offset - info->pos);
1119 }
1120
1121 if (len > 0) {
1122 memcpy(info->buffer + info->pos, data, len);
1123 info->pos += len;
1124 }
1125}
1126
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001127static int
1128mptscsih_copy_info(struct info_str *info, char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129{
1130 va_list args;
1131 char buf[81];
1132 int len;
1133
1134 va_start(args, fmt);
1135 len = vsprintf(buf, fmt, args);
1136 va_end(args);
1137
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001138 mptscsih_copy_mem_info(info, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 return len;
1140}
1141
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001142static int
1143mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144{
1145 struct info_str info;
1146
1147 info.buffer = pbuf;
1148 info.length = len;
1149 info.offset = offset;
1150 info.pos = 0;
1151
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001152 mptscsih_copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name);
1153 mptscsih_copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word);
1154 mptscsih_copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts);
1155 mptscsih_copy_info(&info, "MaxQ=%d\n", ioc->req_depth);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 return ((info.pos > info.offset) ? info.pos - info.offset : 0);
1158}
1159
1160/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1161/**
1162 * mptscsih_proc_info - Return information about MPT adapter
1163 *
1164 * (linux scsi_host_template.info routine)
1165 *
1166 * buffer: if write, user data; if read, buffer for user
1167 * length: if write, return length;
1168 * offset: if write, 0; if read, the current offset into the buffer from
1169 * the previous read.
1170 * hostno: scsi host number
1171 * func: if write = 1; if read = 0
1172 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001173int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
1175 int length, int func)
1176{
1177 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
1178 MPT_ADAPTER *ioc = hd->ioc;
1179 int size = 0;
1180
1181 if (func) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001182 /*
1183 * write is not supported
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 */
1185 } else {
1186 if (start)
1187 *start = buffer;
1188
1189 size = mptscsih_host_info(ioc, buffer, offset, length);
1190 }
1191
1192 return size;
1193}
1194
1195/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1196#define ADD_INDEX_LOG(req_ent) do { } while(0)
1197
1198/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1199/**
1200 * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
1201 * @SCpnt: Pointer to scsi_cmnd structure
1202 * @done: Pointer SCSI mid-layer IO completion function
1203 *
1204 * (linux scsi_host_template.queuecommand routine)
1205 * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
1206 * from a linux scsi_cmnd request and send it to the IOC.
1207 *
1208 * Returns 0. (rtn value discarded by linux scsi mid-layer)
1209 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001210int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
1212{
1213 MPT_SCSI_HOST *hd;
1214 MPT_FRAME_HDR *mf;
1215 SCSIIORequest_t *pScsiReq;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001216 VirtDevice *vdev = SCpnt->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 int lun;
1218 u32 datalen;
1219 u32 scsictl;
1220 u32 scsidir;
1221 u32 cmd_len;
1222 int my_idx;
1223 int ii;
1224
1225 hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 lun = SCpnt->device->lun;
1227 SCpnt->scsi_done = done;
1228
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
1230 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
1231
1232 if (hd->resetPending) {
1233 dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
1234 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
1235 return SCSI_MLQUEUE_HOST_BUSY;
1236 }
1237
Moore, Ericf44e5462006-03-14 09:14:21 -07001238 if ((hd->ioc->bus_type == SPI) &&
1239 vdev->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT &&
James Bottomleyc92f2222006-03-01 09:02:49 -06001240 mptscsih_raid_id_to_num(hd, SCpnt->device->id) < 0) {
1241 SCpnt->result = DID_NO_CONNECT << 16;
1242 done(SCpnt);
1243 return 0;
1244 }
1245
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 /*
1247 * Put together a MPT SCSI request...
1248 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001249 if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
1251 hd->ioc->name));
1252 return SCSI_MLQUEUE_HOST_BUSY;
1253 }
1254
1255 pScsiReq = (SCSIIORequest_t *) mf;
1256
1257 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
1258
1259 ADD_INDEX_LOG(my_idx);
1260
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001261 /* TUR's being issued with scsictl=0x02000000 (DATA_IN)!
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 * Seems we may receive a buffer (datalen>0) even when there
1263 * will be no data transfer! GRRRRR...
1264 */
1265 if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
1266 datalen = SCpnt->request_bufflen;
1267 scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
1268 } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
1269 datalen = SCpnt->request_bufflen;
1270 scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
1271 } else {
1272 datalen = 0;
1273 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
1274 }
1275
1276 /* Default to untagged. Once a target structure has been allocated,
1277 * use the Inquiry data to determine if device supports tagged.
1278 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001279 if (vdev
1280 && (vdev->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 && (SCpnt->device->tagged_supported)) {
1282 scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
1283 } else {
1284 scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
1285 }
1286
1287 /* Use the above information to set up the message frame
1288 */
Moore, Eric914c2d82006-03-14 09:19:36 -07001289 pScsiReq->TargetID = (u8) vdev->vtarget->target_id;
1290 pScsiReq->Bus = vdev->vtarget->bus_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 pScsiReq->ChainOffset = 0;
James Bottomleyc92f2222006-03-01 09:02:49 -06001292 if (vdev->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
1293 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
1294 else
1295 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 pScsiReq->CDBLength = SCpnt->cmd_len;
1297 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
1298 pScsiReq->Reserved = 0;
1299 pScsiReq->MsgFlags = mpt_msg_flags();
1300 pScsiReq->LUN[0] = 0;
1301 pScsiReq->LUN[1] = lun;
1302 pScsiReq->LUN[2] = 0;
1303 pScsiReq->LUN[3] = 0;
1304 pScsiReq->LUN[4] = 0;
1305 pScsiReq->LUN[5] = 0;
1306 pScsiReq->LUN[6] = 0;
1307 pScsiReq->LUN[7] = 0;
1308 pScsiReq->Control = cpu_to_le32(scsictl);
1309
1310 /*
1311 * Write SCSI CDB into the message
1312 */
1313 cmd_len = SCpnt->cmd_len;
1314 for (ii=0; ii < cmd_len; ii++)
1315 pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
1316
1317 for (ii=cmd_len; ii < 16; ii++)
1318 pScsiReq->CDB[ii] = 0;
1319
1320 /* DataLength */
1321 pScsiReq->DataLength = cpu_to_le32(datalen);
1322
1323 /* SenseBuffer low address */
1324 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
1325 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
1326
1327 /* Now add the SG list
1328 * Always have a SGE even if null length.
1329 */
1330 if (datalen == 0) {
1331 /* Add a NULL SGE */
1332 mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
1333 (dma_addr_t) -1);
1334 } else {
1335 /* Add a 32 or 64 bit SGE */
1336 if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS)
1337 goto fail;
1338 }
1339
1340 hd->ScsiLookup[my_idx] = SCpnt;
1341 SCpnt->host_scribble = NULL;
1342
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001343 mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
1345 hd->ioc->name, SCpnt, mf, my_idx));
1346 DBG_DUMP_REQUEST_FRAME(mf)
1347 return 0;
1348
1349 fail:
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001350 hd->ScsiLookup[my_idx] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 mptscsih_freeChainBuffers(hd->ioc, my_idx);
1352 mpt_free_msg_frame(hd->ioc, mf);
1353 return SCSI_MLQUEUE_HOST_BUSY;
1354}
1355
1356/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1357/*
1358 * mptscsih_freeChainBuffers - Function to free chain buffers associated
1359 * with a SCSI IO request
1360 * @hd: Pointer to the MPT_SCSI_HOST instance
1361 * @req_idx: Index of the SCSI IO request frame.
1362 *
1363 * Called if SG chain buffer allocation fails and mptscsih callbacks.
1364 * No return.
1365 */
1366static void
1367mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
1368{
1369 MPT_FRAME_HDR *chain;
1370 unsigned long flags;
1371 int chain_idx;
1372 int next;
1373
1374 /* Get the first chain index and reset
1375 * tracker state.
1376 */
1377 chain_idx = ioc->ReqToChain[req_idx];
1378 ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
1379
1380 while (chain_idx != MPT_HOST_NO_CHAIN) {
1381
1382 /* Save the next chain buffer index */
1383 next = ioc->ChainToChain[chain_idx];
1384
1385 /* Free this chain buffer and reset
1386 * tracker
1387 */
1388 ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
1389
1390 chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer
1391 + (chain_idx * ioc->req_sz));
1392
1393 spin_lock_irqsave(&ioc->FreeQlock, flags);
1394 list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ);
1395 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
1396
1397 dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
1398 ioc->name, chain_idx));
1399
1400 /* handle next */
1401 chain_idx = next;
1402 }
1403 return;
1404}
1405
1406/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1407/*
1408 * Reset Handling
1409 */
1410
1411/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1412/*
1413 * mptscsih_TMHandler - Generic handler for SCSI Task Management.
1414 * Fall through to mpt_HardResetHandler if: not operational, too many
1415 * failed TM requests or handshake failure.
1416 *
1417 * @ioc: Pointer to MPT_ADAPTER structure
1418 * @type: Task Management type
1419 * @target: Logical Target ID for reset (if appropriate)
1420 * @lun: Logical Unit for reset (if appropriate)
1421 * @ctx2abort: Context for the task to be aborted (if appropriate)
1422 *
1423 * Remark: Currently invoked from a non-interrupt thread (_bh).
1424 *
1425 * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
1426 * will be active.
1427 *
1428 * Returns 0 for SUCCESS or -1 if FAILED.
1429 */
James Bottomley663e1aa2006-01-29 12:10:24 -06001430int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1432{
1433 MPT_ADAPTER *ioc;
1434 int rc = -1;
1435 int doTask = 1;
1436 u32 ioc_raw_state;
1437 unsigned long flags;
1438
1439 /* If FW is being reloaded currently, return success to
1440 * the calling function.
1441 */
1442 if (hd == NULL)
1443 return 0;
1444
1445 ioc = hd->ioc;
1446 if (ioc == NULL) {
1447 printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
1448 return FAILED;
1449 }
1450 dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
1451
1452 // SJR - CHECKME - Can we avoid this here?
1453 // (mpt_HardResetHandler has this check...)
1454 spin_lock_irqsave(&ioc->diagLock, flags);
1455 if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
1456 spin_unlock_irqrestore(&ioc->diagLock, flags);
1457 return FAILED;
1458 }
1459 spin_unlock_irqrestore(&ioc->diagLock, flags);
1460
1461 /* Wait a fixed amount of time for the TM pending flag to be cleared.
1462 * If we time out and not bus reset, then we return a FAILED status to the caller.
1463 * The call to mptscsih_tm_pending_wait() will set the pending flag if we are
1464 * successful. Otherwise, reload the FW.
1465 */
1466 if (mptscsih_tm_pending_wait(hd) == FAILED) {
1467 if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001468 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 "Timed out waiting for last TM (%d) to complete! \n",
1470 hd->ioc->name, hd->tmPending));
1471 return FAILED;
1472 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001473 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 "Timed out waiting for last TM (%d) to complete! \n",
1475 hd->ioc->name, hd->tmPending));
1476 return FAILED;
1477 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001478 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 "Timed out waiting for last TM (%d) to complete! \n",
1480 hd->ioc->name, hd->tmPending));
1481 if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
1482 return FAILED;
1483
1484 doTask = 0;
1485 }
1486 } else {
1487 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1488 hd->tmPending |= (1 << type);
1489 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1490 }
1491
1492 /* Is operational?
1493 */
1494 ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
1495
1496#ifdef MPT_DEBUG_RESET
1497 if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
1498 printk(MYIOC_s_WARN_FMT
1499 "TM Handler: IOC Not operational(0x%x)!\n",
1500 hd->ioc->name, ioc_raw_state);
1501 }
1502#endif
1503
1504 if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
1505 && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
1506
1507 /* Isse the Task Mgmt request.
1508 */
1509 if (hd->hard_resets < -1)
1510 hd->hard_resets++;
1511 rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout);
1512 if (rc) {
1513 printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
1514 } else {
1515 dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
1516 }
1517 }
1518
1519 /* Only fall through to the HRH if this is a bus reset
1520 */
1521 if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
1522 ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
1523 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1524 hd->ioc->name));
1525 rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1526 }
1527
1528 dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
1529
1530 return rc;
1531}
1532
1533
1534/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1535/*
1536 * mptscsih_IssueTaskMgmt - Generic send Task Management function.
1537 * @hd: Pointer to MPT_SCSI_HOST structure
1538 * @type: Task Management type
1539 * @target: Logical Target ID for reset (if appropriate)
1540 * @lun: Logical Unit for reset (if appropriate)
1541 * @ctx2abort: Context for the task to be aborted (if appropriate)
1542 *
1543 * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
1544 * or a non-interrupt thread. In the former, must not call schedule().
1545 *
1546 * Not all fields are meaningfull for all task types.
1547 *
1548 * Returns 0 for SUCCESS, -999 for "no msg frames",
1549 * else other non-zero value returned.
1550 */
1551static int
1552mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1553{
1554 MPT_FRAME_HDR *mf;
1555 SCSITaskMgmt_t *pScsiTm;
1556 int ii;
1557 int retval;
1558
1559 /* Return Fail to calling function if no message frames available.
1560 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001561 if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
1563 hd->ioc->name));
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001564 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 }
1566 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
1567 hd->ioc->name, mf));
1568
1569 /* Format the Request
1570 */
1571 pScsiTm = (SCSITaskMgmt_t *) mf;
1572 pScsiTm->TargetID = target;
1573 pScsiTm->Bus = channel;
1574 pScsiTm->ChainOffset = 0;
1575 pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1576
1577 pScsiTm->Reserved = 0;
1578 pScsiTm->TaskType = type;
1579 pScsiTm->Reserved1 = 0;
1580 pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
1581 ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
1582
1583 for (ii= 0; ii < 8; ii++) {
1584 pScsiTm->LUN[ii] = 0;
1585 }
1586 pScsiTm->LUN[1] = lun;
1587
1588 for (ii=0; ii < 7; ii++)
1589 pScsiTm->Reserved2[ii] = 0;
1590
1591 pScsiTm->TaskMsgContext = ctx2abort;
1592
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001593 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n",
1594 hd->ioc->name, ctx2abort, type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
1596 DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm);
1597
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001598 if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 sizeof(SCSITaskMgmt_t), (u32*)pScsiTm,
1600 CAN_SLEEP)) != 0) {
1601 dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!"
1602 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1603 hd->ioc, mf));
1604 mpt_free_msg_frame(hd->ioc, mf);
1605 return retval;
1606 }
1607
1608 if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
1609 dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!"
1610 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1611 hd->ioc, mf));
1612 mpt_free_msg_frame(hd->ioc, mf);
1613 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1614 hd->ioc->name));
1615 retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1616 }
1617
1618 return retval;
1619}
1620
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001621static int
1622mptscsih_get_tm_timeout(MPT_ADAPTER *ioc)
1623{
1624 switch (ioc->bus_type) {
1625 case FC:
1626 return 40;
1627 case SAS:
1628 return 10;
1629 case SPI:
1630 default:
1631 return 2;
1632 }
1633}
1634
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1636/**
1637 * mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant
1638 * @SCpnt: Pointer to scsi_cmnd structure, IO to be aborted
1639 *
1640 * (linux scsi_host_template.eh_abort_handler routine)
1641 *
1642 * Returns SUCCESS or FAILED.
1643 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001644int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645mptscsih_abort(struct scsi_cmnd * SCpnt)
1646{
1647 MPT_SCSI_HOST *hd;
1648 MPT_ADAPTER *ioc;
1649 MPT_FRAME_HDR *mf;
1650 u32 ctx2abort;
1651 int scpnt_idx;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001652 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001653 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
1655 /* If we can't locate our host adapter structure, return FAILED status.
1656 */
1657 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) {
1658 SCpnt->result = DID_RESET << 16;
1659 SCpnt->scsi_done(SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001660 dfailprintk((KERN_INFO MYNAM ": mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 "Can't locate host! (sc=%p)\n",
1662 SCpnt));
1663 return FAILED;
1664 }
1665
1666 ioc = hd->ioc;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001667 if (hd->resetPending) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 return FAILED;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001669 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
1671 if (hd->timeouts < -1)
1672 hd->timeouts++;
1673
1674 /* Find this command
1675 */
1676 if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001677 /* Cmd not found in ScsiLookup.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 * Do OS callback.
1679 */
1680 SCpnt->result = DID_RESET << 16;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001681 dtmprintk((KERN_INFO MYNAM ": %s: mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 "Command not in the active list! (sc=%p)\n",
1683 hd->ioc->name, SCpnt));
1684 return SUCCESS;
1685 }
1686
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001687 printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n",
1688 hd->ioc->name, SCpnt);
1689 scsi_print_command(SCpnt);
1690
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
1692 * (the IO to be ABORT'd)
1693 *
1694 * NOTE: Since we do not byteswap MsgContext, we do not
1695 * swap it here either. It is an opaque cookie to
1696 * the controller, so it does not matter. -DaveM
1697 */
1698 mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
1699 ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
1700
1701 hd->abortSCpnt = SCpnt;
1702
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001703 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001704 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
Moore, Eric914c2d82006-03-14 09:19:36 -07001705 vdev->vtarget->bus_id, vdev->vtarget->target_id, vdev->lun,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001706 ctx2abort, mptscsih_get_tm_timeout(ioc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001708 printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n",
1709 hd->ioc->name,
1710 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001712 if (retval == 0)
1713 return SUCCESS;
1714
1715 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 hd->tmPending = 0;
1717 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001719 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720}
1721
1722/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1723/**
1724 * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
1725 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1726 *
1727 * (linux scsi_host_template.eh_dev_reset_handler routine)
1728 *
1729 * Returns SUCCESS or FAILED.
1730 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001731int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
1733{
1734 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001735 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001736 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737
1738 /* If we can't locate our host adapter structure, return FAILED status.
1739 */
1740 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001741 dtmprintk((KERN_INFO MYNAM ": mptscsih_dev_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 "Can't locate host! (sc=%p)\n",
1743 SCpnt));
1744 return FAILED;
1745 }
1746
1747 if (hd->resetPending)
1748 return FAILED;
1749
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001750 printk(KERN_WARNING MYNAM ": %s: attempting target reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001752 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001754 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001755 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
Moore, Eric914c2d82006-03-14 09:19:36 -07001756 vdev->vtarget->bus_id, vdev->vtarget->target_id,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001757 0, 0, mptscsih_get_tm_timeout(hd->ioc));
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001758
1759 printk (KERN_WARNING MYNAM ": %s: target reset: %s (sc=%p)\n",
1760 hd->ioc->name,
1761 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1762
1763 if (retval == 0)
1764 return SUCCESS;
1765
1766 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 hd->tmPending = 0;
1768 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001770 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771}
1772
1773/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1774/**
1775 * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
1776 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1777 *
1778 * (linux scsi_host_template.eh_bus_reset_handler routine)
1779 *
1780 * Returns SUCCESS or FAILED.
1781 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001782int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
1784{
1785 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001786 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001787 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788
1789 /* If we can't locate our host adapter structure, return FAILED status.
1790 */
1791 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001792 dtmprintk((KERN_INFO MYNAM ": mptscsih_bus_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 "Can't locate host! (sc=%p)\n",
1794 SCpnt ) );
1795 return FAILED;
1796 }
1797
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001798 printk(KERN_WARNING MYNAM ": %s: attempting bus reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001800 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801
1802 if (hd->timeouts < -1)
1803 hd->timeouts++;
1804
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001805 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001806 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
Moore, Eric914c2d82006-03-14 09:19:36 -07001807 vdev->vtarget->bus_id, 0, 0, 0, mptscsih_get_tm_timeout(hd->ioc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001809 printk (KERN_WARNING MYNAM ": %s: bus reset: %s (sc=%p)\n",
1810 hd->ioc->name,
1811 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1812
1813 if (retval == 0)
1814 return SUCCESS;
1815
1816 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 hd->tmPending = 0;
1818 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001820 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821}
1822
1823/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1824/**
1825 * mptscsih_host_reset - Perform a SCSI host adapter RESET!
1826 * new_eh variant
1827 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1828 *
1829 * (linux scsi_host_template.eh_host_reset_handler routine)
1830 *
1831 * Returns SUCCESS or FAILED.
1832 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001833int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834mptscsih_host_reset(struct scsi_cmnd *SCpnt)
1835{
1836 MPT_SCSI_HOST * hd;
1837 int status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838
1839 /* If we can't locate the host to reset, then we failed. */
1840 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001841 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 "Can't locate host! (sc=%p)\n",
1843 SCpnt ) );
1844 return FAILED;
1845 }
1846
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001847 printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 hd->ioc->name, SCpnt);
1849
1850 /* If our attempts to reset the host failed, then return a failed
1851 * status. The host will be taken off line by the SCSI mid-layer.
1852 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
1854 status = FAILED;
1855 } else {
1856 /* Make sure TM pending is cleared and TM state is set to
1857 * NONE.
1858 */
1859 hd->tmPending = 0;
1860 hd->tmState = TM_STATE_NONE;
1861 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001863 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 "Status = %s\n",
1865 (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
1866
1867 return status;
1868}
1869
1870/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1871/**
1872 * mptscsih_tm_pending_wait - wait for pending task management request to
1873 * complete.
1874 * @hd: Pointer to MPT host structure.
1875 *
1876 * Returns {SUCCESS,FAILED}.
1877 */
1878static int
1879mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
1880{
1881 unsigned long flags;
1882 int loop_count = 4 * 10; /* Wait 10 seconds */
1883 int status = FAILED;
1884
1885 do {
1886 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1887 if (hd->tmState == TM_STATE_NONE) {
1888 hd->tmState = TM_STATE_IN_PROGRESS;
1889 hd->tmPending = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001891 status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 break;
1893 }
1894 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1895 msleep(250);
1896 } while (--loop_count);
1897
1898 return status;
1899}
1900
1901/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1902/**
1903 * mptscsih_tm_wait_for_completion - wait for completion of TM task
1904 * @hd: Pointer to MPT host structure.
1905 *
1906 * Returns {SUCCESS,FAILED}.
1907 */
1908static int
1909mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
1910{
1911 unsigned long flags;
1912 int loop_count = 4 * timeout;
1913 int status = FAILED;
1914
1915 do {
1916 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1917 if(hd->tmPending == 0) {
1918 status = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001919 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 break;
1921 }
1922 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1923 msleep_interruptible(250);
1924 } while (--loop_count);
1925
1926 return status;
1927}
1928
1929/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric9f63bb72006-01-16 18:53:26 -07001930static void
1931mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
1932{
1933 char *desc;
1934
1935 switch (response_code) {
1936 case MPI_SCSITASKMGMT_RSP_TM_COMPLETE:
1937 desc = "The task completed.";
1938 break;
1939 case MPI_SCSITASKMGMT_RSP_INVALID_FRAME:
1940 desc = "The IOC received an invalid frame status.";
1941 break;
1942 case MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
1943 desc = "The task type is not supported.";
1944 break;
1945 case MPI_SCSITASKMGMT_RSP_TM_FAILED:
1946 desc = "The requested task failed.";
1947 break;
1948 case MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED:
1949 desc = "The task completed successfully.";
1950 break;
1951 case MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN:
1952 desc = "The LUN request is invalid.";
1953 break;
1954 case MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
1955 desc = "The task is in the IOC queue and has not been sent to target.";
1956 break;
1957 default:
1958 desc = "unknown";
1959 break;
1960 }
1961 printk(MYIOC_s_INFO_FMT "Response Code(0x%08x): F/W: %s\n",
1962 ioc->name, response_code, desc);
1963}
1964
1965/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966/**
1967 * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
1968 * @ioc: Pointer to MPT_ADAPTER structure
1969 * @mf: Pointer to SCSI task mgmt request frame
1970 * @mr: Pointer to SCSI task mgmt reply frame
1971 *
1972 * This routine is called from mptbase.c::mpt_interrupt() at the completion
1973 * of any SCSI task management request.
1974 * This routine is registered with the MPT (base) driver at driver
1975 * load/init time via the mpt_register() API call.
1976 *
1977 * Returns 1 indicating alloc'd request frame ptr should be freed.
1978 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001979int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
1981{
1982 SCSITaskMgmtReply_t *pScsiTmReply;
1983 SCSITaskMgmt_t *pScsiTmReq;
1984 MPT_SCSI_HOST *hd;
1985 unsigned long flags;
1986 u16 iocstatus;
1987 u8 tmType;
1988
1989 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
1990 ioc->name, mf, mr));
1991 if (ioc->sh) {
1992 /* Depending on the thread, a timer is activated for
1993 * the TM request. Delete this timer on completion of TM.
1994 * Decrement count of outstanding TM requests.
1995 */
1996 hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
1997 } else {
1998 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
1999 ioc->name));
2000 return 1;
2001 }
2002
2003 if (mr == NULL) {
2004 dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
2005 ioc->name, mf));
2006 return 1;
2007 } else {
2008 pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
2009 pScsiTmReq = (SCSITaskMgmt_t*)mf;
2010
2011 /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
2012 tmType = pScsiTmReq->TaskType;
2013
Moore, Eric9f63bb72006-01-16 18:53:26 -07002014 if (ioc->facts.MsgVersion >= MPI_VERSION_01_05 &&
2015 pScsiTmReply->ResponseCode)
2016 mptscsih_taskmgmt_response_code(ioc,
2017 pScsiTmReply->ResponseCode);
2018
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n",
2020 ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
2021 DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply);
2022
2023 iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2024 dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n",
2025 ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo)));
2026 /* Error? (anything non-zero?) */
2027 if (iocstatus) {
2028
2029 /* clear flags and continue.
2030 */
2031 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
2032 hd->abortSCpnt = NULL;
2033
2034 /* If an internal command is present
2035 * or the TM failed - reload the FW.
2036 * FC FW may respond FAILED to an ABORT
2037 */
2038 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
2039 if ((hd->cmdPtr) ||
2040 (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
2041 if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
2042 printk((KERN_WARNING
2043 " Firmware Reload FAILED!!\n"));
2044 }
2045 }
2046 }
2047 } else {
2048 dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name));
2049
2050 hd->abortSCpnt = NULL;
2051
2052 }
2053 }
2054
2055 spin_lock_irqsave(&ioc->FreeQlock, flags);
2056 hd->tmPending = 0;
2057 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2058 hd->tmState = TM_STATE_NONE;
2059
2060 return 1;
2061}
2062
2063/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2064/*
2065 * This is anyones guess quite frankly.
2066 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002067int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
2069 sector_t capacity, int geom[])
2070{
2071 int heads;
2072 int sectors;
2073 sector_t cylinders;
2074 ulong dummy;
2075
2076 heads = 64;
2077 sectors = 32;
2078
2079 dummy = heads * sectors;
2080 cylinders = capacity;
2081 sector_div(cylinders,dummy);
2082
2083 /*
2084 * Handle extended translation size for logical drives
2085 * > 1Gb
2086 */
2087 if ((ulong)capacity >= 0x200000) {
2088 heads = 255;
2089 sectors = 63;
2090 dummy = heads * sectors;
2091 cylinders = capacity;
2092 sector_div(cylinders,dummy);
2093 }
2094
2095 /* return result */
2096 geom[0] = heads;
2097 geom[1] = sectors;
2098 geom[2] = cylinders;
2099
2100 dprintk((KERN_NOTICE
2101 ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n",
2102 sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors));
2103
2104 return 0;
2105}
2106
Moore, Ericf44e5462006-03-14 09:14:21 -07002107/* Search IOC page 3 to determine if this is hidden physical disk
2108 *
2109 */
2110int
2111mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
2112{
2113 int i;
2114
2115 if (!ioc->raid_data.isRaid || !ioc->raid_data.pIocPg3)
2116 return 0;
2117 for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
2118 if (id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID)
2119 return 1;
2120 }
2121 return 0;
2122}
2123EXPORT_SYMBOL(mptscsih_is_phys_disk);
2124
James Bottomleyc92f2222006-03-01 09:02:49 -06002125int
2126mptscsih_raid_id_to_num(MPT_SCSI_HOST *hd, uint physdiskid)
2127{
2128 int i;
2129
2130 if (!hd->ioc->raid_data.isRaid || !hd->ioc->raid_data.pIocPg3)
2131 return -ENXIO;
2132
2133 for (i = 0; i < hd->ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
2134 if (physdiskid ==
2135 hd->ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID)
2136 return hd->ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum;
2137 }
2138
2139 return -ENXIO;
2140}
2141EXPORT_SYMBOL(mptscsih_raid_id_to_num);
2142
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2144/*
2145 * OS entry point to allow host driver to alloc memory
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002146 * for each scsi target. Called once per device the bus scan.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 * Return non-zero if allocation fails.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002149int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002150mptscsih_target_alloc(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002152 VirtTarget *vtarget;
2153
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +01002154 vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002155 if (!vtarget)
2156 return -ENOMEM;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002157 starget->hostdata = vtarget;
James Bottomleyc92f2222006-03-01 09:02:49 -06002158 vtarget->starget = starget;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002159 return 0;
2160}
2161
2162/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2163/*
2164 * OS entry point to allow host driver to alloc memory
2165 * for each scsi device. Called once per device the bus scan.
2166 * Return non-zero if allocation fails.
2167 */
2168int
2169mptscsih_slave_alloc(struct scsi_device *sdev)
2170{
2171 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002173 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 VirtDevice *vdev;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002175 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +01002177 vdev = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 if (!vdev) {
2179 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
2180 hd->ioc->name, sizeof(VirtDevice));
2181 return -ENOMEM;
2182 }
2183
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002184 vdev->lun = sdev->lun;
2185 sdev->hostdata = vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002187 starget = scsi_target(sdev);
2188 vtarget = starget->hostdata;
James Bottomleyc92f2222006-03-01 09:02:49 -06002189
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002190 vdev->vtarget = vtarget;
2191
2192 if (vtarget->num_luns == 0) {
2193 hd->Targets[sdev->id] = vtarget;
2194 vtarget->ioc_id = hd->ioc->id;
2195 vtarget->tflags = MPT_TARGET_FLAGS_Q_YES;
2196 vtarget->target_id = sdev->id;
2197 vtarget->bus_id = sdev->channel;
James Bottomleyc92f2222006-03-01 09:02:49 -06002198 if (hd->ioc->bus_type == SPI && sdev->channel == 0 &&
2199 hd->ioc->raid_data.isRaid & (1 << sdev->id)) {
2200 vtarget->raidVolume = 1;
2201 ddvtprintk((KERN_INFO
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002202 "RAID Volume @ id %d\n", sdev->id));
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002203 }
2204 }
2205 vtarget->num_luns++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 return 0;
2207}
2208
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209/*
2210 * OS entry point to allow for host driver to free allocated memory
2211 * Called if no device present or device being unloaded
2212 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002213void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002214mptscsih_target_destroy(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002216 if (starget->hostdata)
2217 kfree(starget->hostdata);
2218 starget->hostdata = NULL;
2219}
2220
2221/*
2222 * OS entry point to allow for host driver to free allocated memory
2223 * Called if no device present or device being unloaded
2224 */
2225void
2226mptscsih_slave_destroy(struct scsi_device *sdev)
2227{
2228 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002230 VirtTarget *vtarget;
2231 VirtDevice *vdevice;
2232 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002234 starget = scsi_target(sdev);
2235 vtarget = starget->hostdata;
2236 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002238 mptscsih_search_running_cmds(hd, vdevice);
2239 vtarget->luns[0] &= ~(1 << vdevice->lun);
2240 vtarget->num_luns--;
2241 if (vtarget->num_luns == 0) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002242 hd->Targets[sdev->id] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002244 mptscsih_synchronize_cache(hd, vdevice);
2245 kfree(vdevice);
2246 sdev->hostdata = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247}
2248
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002249/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2250/*
2251 * mptscsih_change_queue_depth - This function will set a devices queue depth
2252 * @sdev: per scsi_device pointer
2253 * @qdepth: requested queue depth
2254 *
2255 * Adding support for new 'change_queue_depth' api.
2256*/
2257int
2258mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002260 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
2261 VirtTarget *vtarget;
2262 struct scsi_target *starget;
2263 int max_depth;
2264 int tagged;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002266 starget = scsi_target(sdev);
2267 vtarget = starget->hostdata;
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002268
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002269 if (hd->ioc->bus_type == SPI) {
James Bottomleyc92f2222006-03-01 09:02:49 -06002270 if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 max_depth = 1;
James Bottomleyc92f2222006-03-01 09:02:49 -06002272 else if (sdev->type == TYPE_DISK &&
2273 vtarget->minSyncFactor <= MPT_ULTRA160)
2274 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2275 else
2276 max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 } else
2278 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2279
2280 if (qdepth > max_depth)
2281 qdepth = max_depth;
2282 if (qdepth == 1)
2283 tagged = 0;
2284 else
2285 tagged = MSG_SIMPLE_TAG;
2286
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002287 scsi_adjust_queue_depth(sdev, tagged, qdepth);
2288 return sdev->queue_depth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289}
2290
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291/*
2292 * OS entry point to adjust the queue_depths on a per-device basis.
2293 * Called once per device the bus scan. Use it to force the queue_depth
2294 * member to 1 if a device does not support Q tags.
2295 * Return non-zero if fails.
2296 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002297int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002298mptscsih_slave_configure(struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002300 struct Scsi_Host *sh = sdev->host;
2301 VirtTarget *vtarget;
2302 VirtDevice *vdevice;
2303 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sh->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002305 int indexed_lun, lun_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002307 starget = scsi_target(sdev);
2308 vtarget = starget->hostdata;
2309 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
2311 dsprintk((MYIOC_s_INFO_FMT
2312 "device @ %p, id=%d, LUN=%d, channel=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002313 hd->ioc->name, sdev, sdev->id, sdev->lun, sdev->channel));
2314 if (hd->ioc->bus_type == SPI)
2315 dsprintk((MYIOC_s_INFO_FMT
2316 "sdtr %d wdtr %d ppr %d inq length=%d\n",
2317 hd->ioc->name, sdev->sdtr, sdev->wdtr,
2318 sdev->ppr, sdev->inquiry_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002320 if (sdev->id > sh->max_id) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 /* error case, should never happen */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002322 scsi_adjust_queue_depth(sdev, 0, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 goto slave_configure_exit;
2324 }
2325
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002326 vdevice->configured_lun=1;
2327 lun_index = (vdevice->lun >> 5); /* 32 luns per lun_index */
2328 indexed_lun = (vdevice->lun % 32);
2329 vtarget->luns[lun_index] |= (1 << indexed_lun);
James Bottomleyc92f2222006-03-01 09:02:49 -06002330 mptscsih_initTarget(hd, vtarget, sdev);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002331 mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332
2333 dsprintk((MYIOC_s_INFO_FMT
2334 "Queue depth=%d, tflags=%x\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002335 hd->ioc->name, sdev->queue_depth, vtarget->tflags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002337 if (hd->ioc->bus_type == SPI)
2338 dsprintk((MYIOC_s_INFO_FMT
2339 "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n",
2340 hd->ioc->name, vtarget->negoFlags, vtarget->maxOffset,
2341 vtarget->minSyncFactor));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342
2343slave_configure_exit:
2344
2345 dsprintk((MYIOC_s_INFO_FMT
2346 "tagged %d, simple %d, ordered %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002347 hd->ioc->name,sdev->tagged_supported, sdev->simple_tags,
2348 sdev->ordered_tags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349
2350 return 0;
2351}
2352
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2354/*
2355 * Private routines...
2356 */
2357
2358/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2359/* Utility function to copy sense data from the scsi_cmnd buffer
2360 * to the FC and SCSI target structures.
2361 *
2362 */
2363static void
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002364mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002366 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 SCSIIORequest_t *pReq;
2368 u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369
2370 /* Get target structure
2371 */
2372 pReq = (SCSIIORequest_t *) mf;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002373 vdev = sc->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
2375 if (sense_count) {
2376 u8 *sense_data;
2377 int req_index;
2378
2379 /* Copy the sense received into the scsi command block. */
2380 req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2381 sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
2382 memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
2383
2384 /* Log SMART data (asc = 0x5D, non-IM case only) if required.
2385 */
2386 if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002387 if ((sense_data[12] == 0x5D) && (vdev->vtarget->raidVolume == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 int idx;
2389 MPT_ADAPTER *ioc = hd->ioc;
2390
Moore, Eric5b5ef4f2006-02-02 17:19:40 -07002391 idx = ioc->eventContext % MPTCTL_EVENT_LOG_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
2393 ioc->events[idx].eventContext = ioc->eventContext;
2394
2395 ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
2396 (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002397 (sc->device->channel << 8) || sc->device->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398
2399 ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
2400
2401 ioc->eventContext++;
2402 }
2403 }
2404 } else {
2405 dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
2406 hd->ioc->name));
2407 }
2408}
2409
2410static u32
2411SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
2412{
2413 MPT_SCSI_HOST *hd;
2414 int i;
2415
2416 hd = (MPT_SCSI_HOST *) sc->device->host->hostdata;
2417
2418 for (i = 0; i < hd->ioc->req_depth; i++) {
2419 if (hd->ScsiLookup[i] == sc) {
2420 return i;
2421 }
2422 }
2423
2424 return -1;
2425}
2426
2427/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002428int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
2430{
2431 MPT_SCSI_HOST *hd;
2432 unsigned long flags;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002433 int ii;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434
2435 dtmprintk((KERN_WARNING MYNAM
2436 ": IOC %s_reset routed to SCSI host driver!\n",
2437 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
2438 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
2439
2440 /* If a FW reload request arrives after base installed but
2441 * before all scsi hosts have been attached, then an alt_ioc
2442 * may have a NULL sh pointer.
2443 */
2444 if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
2445 return 0;
2446 else
2447 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2448
2449 if (reset_phase == MPT_IOC_SETUP_RESET) {
2450 dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
2451
2452 /* Clean Up:
2453 * 1. Set Hard Reset Pending Flag
2454 * All new commands go to doneQ
2455 */
2456 hd->resetPending = 1;
2457
2458 } else if (reset_phase == MPT_IOC_PRE_RESET) {
2459 dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
2460
2461 /* 2. Flush running commands
2462 * Clean ScsiLookup (and associated memory)
2463 * AND clean mytaskQ
2464 */
2465
2466 /* 2b. Reply to OS all known outstanding I/O commands.
2467 */
2468 mptscsih_flush_running_cmds(hd);
2469
2470 /* 2c. If there was an internal command that
2471 * has not completed, configuration or io request,
2472 * free these resources.
2473 */
2474 if (hd->cmdPtr) {
2475 del_timer(&hd->timer);
2476 mpt_free_msg_frame(ioc, hd->cmdPtr);
2477 }
2478
2479 dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
2480
2481 } else {
2482 dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
2483
2484 /* Once a FW reload begins, all new OS commands are
2485 * redirected to the doneQ w/ a reset status.
2486 * Init all control structures.
2487 */
2488
2489 /* ScsiLookup initialization
2490 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002491 for (ii=0; ii < hd->ioc->req_depth; ii++)
2492 hd->ScsiLookup[ii] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
2494 /* 2. Chain Buffer initialization
2495 */
2496
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002497 /* 4. Renegotiate to all devices, if SPI
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499
2500 /* 5. Enable new commands to be posted
2501 */
2502 spin_lock_irqsave(&ioc->FreeQlock, flags);
2503 hd->tmPending = 0;
2504 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2505 hd->resetPending = 0;
2506 hd->tmState = TM_STATE_NONE;
2507
2508 /* 6. If there was an internal command,
2509 * wake this process up.
2510 */
2511 if (hd->cmdPtr) {
2512 /*
2513 * Wake up the original calling thread
2514 */
2515 hd->pLocal = &hd->localReply;
2516 hd->pLocal->completion = MPT_SCANDV_DID_RESET;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002517 hd->scandv_wait_done = 1;
2518 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 hd->cmdPtr = NULL;
2520 }
2521
Michael Reed05e8ec12006-01-13 14:31:54 -06002522 /* 7. FC: Rescan for blocked rports which might have returned.
2523 */
2524 else if (ioc->bus_type == FC) {
2525 int work_count;
2526 unsigned long flags;
2527
2528 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
2529 work_count = ++ioc->fc_rescan_work_count;
2530 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
2531 if (work_count == 1)
2532 schedule_work(&ioc->fc_rescan_work);
2533 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
2535
2536 }
2537
2538 return 1; /* currently means nothing really */
2539}
2540
2541/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002542int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
2544{
2545 MPT_SCSI_HOST *hd;
2546 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
Michael Reed05e8ec12006-01-13 14:31:54 -06002547 int work_count;
2548 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549
Moore, Eric3a892be2006-03-14 09:14:03 -07002550 devtverboseprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 ioc->name, event));
2552
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002553 if (ioc->sh == NULL ||
2554 ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL))
2555 return 1;
2556
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 switch (event) {
2558 case MPI_EVENT_UNIT_ATTENTION: /* 03 */
2559 /* FIXME! */
2560 break;
2561 case MPI_EVENT_IOC_BUS_RESET: /* 04 */
2562 case MPI_EVENT_EXT_BUS_RESET: /* 05 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002563 if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1))
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002564 hd->soft_resets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 break;
2566 case MPI_EVENT_LOGOUT: /* 09 */
2567 /* FIXME! */
2568 break;
2569
Michael Reed05e8ec12006-01-13 14:31:54 -06002570 case MPI_EVENT_RESCAN: /* 06 */
2571 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
2572 work_count = ++ioc->fc_rescan_work_count;
2573 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
2574 if (work_count == 1)
2575 schedule_work(&ioc->fc_rescan_work);
2576 break;
2577
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 /*
2579 * CHECKME! Don't think we need to do
2580 * anything for these, but...
2581 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582 case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
2583 case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
2584 /*
2585 * CHECKME! Falling thru...
2586 */
2587 break;
2588
2589 case MPI_EVENT_INTEGRATED_RAID: /* 0B */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002590 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 case MPI_EVENT_NONE: /* 00 */
2593 case MPI_EVENT_LOG_DATA: /* 01 */
2594 case MPI_EVENT_STATE_CHANGE: /* 02 */
2595 case MPI_EVENT_EVENT_CHANGE: /* 0A */
2596 default:
2597 dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
2598 break;
2599 }
2600
2601 return 1; /* currently means nothing really */
2602}
2603
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2605/*
2606 * mptscsih_initTarget - Target, LUN alloc/free functionality.
2607 * @hd: Pointer to MPT_SCSI_HOST structure
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002608 * @vtarget: per target private data
James Bottomleyc92f2222006-03-01 09:02:49 -06002609 * @sdev: SCSI device
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 *
2611 * NOTE: It's only SAFE to call this routine if data points to
2612 * sane & valid STANDARD INQUIRY data!
2613 *
2614 * Allocate and initialize memory for this target.
2615 * Save inquiry data.
2616 *
2617 */
2618static void
James Bottomleyc92f2222006-03-01 09:02:49 -06002619mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget,
2620 struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002623 hd->ioc->name, vtarget->bus_id, vtarget->target_id, lun, hd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 /* Is LUN supported? If so, upper 2 bits will be 0
2626 * in first byte of inquiry data.
2627 */
James Bottomleyc92f2222006-03-01 09:02:49 -06002628 if (sdev->inq_periph_qual != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 return;
2630
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002631 if (vtarget == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633
James Bottomleyc92f2222006-03-01 09:02:49 -06002634 vtarget->type = sdev->type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002636 if (hd->ioc->bus_type != SPI)
2637 return;
2638
James Bottomleyc92f2222006-03-01 09:02:49 -06002639 if ((sdev->type == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002640 /* Treat all Processors as SAF-TE if
2641 * command line option is set */
2642 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2643 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
James Bottomleyc92f2222006-03-01 09:02:49 -06002644 }else if ((sdev->type == TYPE_PROCESSOR) &&
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002645 !(vtarget->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) {
James Bottomleyc92f2222006-03-01 09:02:49 -06002646 if (sdev->inquiry_len > 49 ) {
2647 if (sdev->inquiry[44] == 'S' &&
2648 sdev->inquiry[45] == 'A' &&
2649 sdev->inquiry[46] == 'F' &&
2650 sdev->inquiry[47] == '-' &&
2651 sdev->inquiry[48] == 'T' &&
2652 sdev->inquiry[49] == 'E' ) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002653 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2654 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655 }
2656 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002657 }
James Bottomleyc92f2222006-03-01 09:02:49 -06002658 mptscsih_setTargetNegoParms(hd, vtarget, sdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659}
2660
2661/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2662/*
2663 * Update the target negotiation parameters based on the
2664 * the Inquiry data, adapter capabilities, and NVRAM settings.
2665 *
2666 */
2667static void
James Bottomleyc92f2222006-03-01 09:02:49 -06002668mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *target,
2669 struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670{
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002671 SpiCfgData *pspi_data = &hd->ioc->spi_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672 int id = (int) target->target_id;
2673 int nvram;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674 u8 width = MPT_NARROW;
2675 u8 factor = MPT_ASYNC;
2676 u8 offset = 0;
James Bottomleyc92f2222006-03-01 09:02:49 -06002677 u8 nfactor;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 u8 noQas = 1;
2679
2680 target->negoFlags = pspi_data->noQas;
2681
James Bottomleyc92f2222006-03-01 09:02:49 -06002682 /* noQas == 0 => device supports QAS. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683
James Bottomleyc92f2222006-03-01 09:02:49 -06002684 if (sdev->scsi_level < SCSI_2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685 width = 0;
2686 factor = MPT_ULTRA2;
2687 offset = pspi_data->maxSyncOffset;
2688 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2689 } else {
James Bottomleyc92f2222006-03-01 09:02:49 -06002690 if (scsi_device_wide(sdev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691 width = 1;
2692 }
2693
James Bottomleyc92f2222006-03-01 09:02:49 -06002694 if (scsi_device_sync(sdev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 factor = pspi_data->minSyncFactor;
James Bottomleyc92f2222006-03-01 09:02:49 -06002696 if (!scsi_device_dt(sdev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697 factor = MPT_ULTRA2;
James Bottomleyc92f2222006-03-01 09:02:49 -06002698 else {
2699 if (!scsi_device_ius(sdev) &&
2700 !scsi_device_qas(sdev))
2701 factor = MPT_ULTRA160;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 else {
James Bottomleyc92f2222006-03-01 09:02:49 -06002703 factor = MPT_ULTRA320;
2704 if (scsi_device_qas(sdev)) {
2705 ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id));
2706 noQas = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 }
James Bottomleyc92f2222006-03-01 09:02:49 -06002708 if (sdev->type == TYPE_TAPE &&
2709 scsi_device_ius(sdev))
2710 target->negoFlags |= MPT_TAPE_NEGO_IDP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 offset = pspi_data->maxSyncOffset;
2714
2715 /* If RAID, never disable QAS
2716 * else if non RAID, do not disable
2717 * QAS if bit 1 is set
2718 * bit 1 QAS support, non-raid only
2719 * bit 0 IU support
2720 */
2721 if (target->raidVolume == 1) {
2722 noQas = 0;
2723 }
2724 } else {
2725 factor = MPT_ASYNC;
2726 offset = 0;
2727 }
2728 }
2729
James Bottomleyc92f2222006-03-01 09:02:49 -06002730 if (!sdev->tagged_supported) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2732 }
2733
2734 /* Update tflags based on NVRAM settings. (SCSI only)
2735 */
2736 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
2737 nvram = pspi_data->nvram[id];
2738 nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
2739
2740 if (width)
2741 width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
2742
2743 if (offset > 0) {
2744 /* Ensure factor is set to the
2745 * maximum of: adapter, nvram, inquiry
2746 */
2747 if (nfactor) {
2748 if (nfactor < pspi_data->minSyncFactor )
2749 nfactor = pspi_data->minSyncFactor;
2750
2751 factor = max(factor, nfactor);
2752 if (factor == MPT_ASYNC)
2753 offset = 0;
2754 } else {
2755 offset = 0;
2756 factor = MPT_ASYNC;
2757 }
2758 } else {
2759 factor = MPT_ASYNC;
2760 }
2761 }
2762
2763 /* Make sure data is consistent
2764 */
2765 if ((!width) && (factor < MPT_ULTRA2)) {
2766 factor = MPT_ULTRA2;
2767 }
2768
2769 /* Save the data to the target structure.
2770 */
2771 target->minSyncFactor = factor;
2772 target->maxOffset = offset;
2773 target->maxWidth = width;
2774
2775 target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
2776
2777 /* Disable unused features.
2778 */
2779 if (!width)
2780 target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
2781
2782 if (!offset)
2783 target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
2784
2785 if ( factor > MPT_ULTRA320 )
2786 noQas = 0;
2787
James Bottomleyc92f2222006-03-01 09:02:49 -06002788 if (noQas && (pspi_data->noQas == 0)) {
2789 pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS;
2790 target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791
James Bottomleyc92f2222006-03-01 09:02:49 -06002792 /* Disable QAS in a mixed configuration case
2793 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794
James Bottomleyc92f2222006-03-01 09:02:49 -06002795 ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 }
2797}
2798
2799/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2802/*
2803 * SCSI Config Page functionality ...
2804 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805
2806/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807/* mptscsih_writeIOCPage4 - write IOC Page 4
2808 * @hd: Pointer to a SCSI Host Structure
2809 * @target_id: write IOC Page4 for this ID & Bus
2810 *
2811 * Return: -EAGAIN if unable to obtain a Message Frame
2812 * or 0 if success.
2813 *
2814 * Remark: We do not wait for a return, write pages sequentially.
2815 */
2816static int
2817mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus)
2818{
2819 MPT_ADAPTER *ioc = hd->ioc;
2820 Config_t *pReq;
2821 IOCPage4_t *IOCPage4Ptr;
2822 MPT_FRAME_HDR *mf;
2823 dma_addr_t dataDma;
2824 u16 req_idx;
2825 u32 frameOffset;
2826 u32 flagsLength;
2827 int ii;
2828
2829 /* Get a MF for this command.
2830 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002831 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002832 dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 ioc->name));
2834 return -EAGAIN;
2835 }
2836
2837 /* Set the request and the data pointers.
2838 * Place data at end of MF.
2839 */
2840 pReq = (Config_t *)mf;
2841
2842 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2843 frameOffset = ioc->req_sz - sizeof(IOCPage4_t);
2844
2845 /* Complete the request frame (same for all requests).
2846 */
2847 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
2848 pReq->Reserved = 0;
2849 pReq->ChainOffset = 0;
2850 pReq->Function = MPI_FUNCTION_CONFIG;
2851 pReq->ExtPageLength = 0;
2852 pReq->ExtPageType = 0;
2853 pReq->MsgFlags = 0;
2854 for (ii=0; ii < 8; ii++) {
2855 pReq->Reserved2[ii] = 0;
2856 }
2857
2858 IOCPage4Ptr = ioc->spi_data.pIocPg4;
2859 dataDma = ioc->spi_data.IocPg4_dma;
2860 ii = IOCPage4Ptr->ActiveSEP++;
2861 IOCPage4Ptr->SEP[ii].SEPTargetID = target_id;
2862 IOCPage4Ptr->SEP[ii].SEPBus = bus;
2863 pReq->Header = IOCPage4Ptr->Header;
2864 pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 ));
2865
2866 /* Add a SGE to the config request.
2867 */
2868 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
2869 (IOCPage4Ptr->Header.PageLength + ii) * 4;
2870
2871 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
2872
2873 dinitprintk((MYIOC_s_INFO_FMT
2874 "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
2875 ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus));
2876
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002877 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
2879 return 0;
2880}
2881
2882/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2883/*
2884 * Bus Scan and Domain Validation functionality ...
2885 */
2886
2887/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2888/*
2889 * mptscsih_scandv_complete - Scan and DV callback routine registered
2890 * to Fustion MPT (base) driver.
2891 *
2892 * @ioc: Pointer to MPT_ADAPTER structure
2893 * @mf: Pointer to original MPT request frame
2894 * @mr: Pointer to MPT reply frame (NULL if TurboReply)
2895 *
2896 * This routine is called from mpt.c::mpt_interrupt() at the completion
2897 * of any SCSI IO request.
2898 * This routine is registered with the Fusion MPT (base) driver at driver
2899 * load/init time via the mpt_register() API call.
2900 *
2901 * Returns 1 indicating alloc'd request frame ptr should be freed.
2902 *
2903 * Remark: Sets a completion code and (possibly) saves sense data
2904 * in the IOC member localReply structure.
2905 * Used ONLY for DV and other internal commands.
2906 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002907int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
2909{
2910 MPT_SCSI_HOST *hd;
2911 SCSIIORequest_t *pReq;
2912 int completionCode;
2913 u16 req_idx;
2914
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002915 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2916
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 if ((mf == NULL) ||
2918 (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
2919 printk(MYIOC_s_ERR_FMT
2920 "ScanDvComplete, %s req frame ptr! (=%p)\n",
2921 ioc->name, mf?"BAD":"NULL", (void *) mf);
2922 goto wakeup;
2923 }
2924
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925 del_timer(&hd->timer);
2926 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2927 hd->ScsiLookup[req_idx] = NULL;
2928 pReq = (SCSIIORequest_t *) mf;
2929
2930 if (mf != hd->cmdPtr) {
2931 printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
2932 hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
2933 }
2934 hd->cmdPtr = NULL;
2935
2936 ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
2937 hd->ioc->name, mf, mr, req_idx));
2938
2939 hd->pLocal = &hd->localReply;
2940 hd->pLocal->scsiStatus = 0;
2941
2942 /* If target struct exists, clear sense valid flag.
2943 */
2944 if (mr == NULL) {
2945 completionCode = MPT_SCANDV_GOOD;
2946 } else {
2947 SCSIIOReply_t *pReply;
2948 u16 status;
2949 u8 scsi_status;
2950
2951 pReply = (SCSIIOReply_t *) mr;
2952
2953 status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2954 scsi_status = pReply->SCSIStatus;
2955
2956 ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
2957 status, pReply->SCSIState, scsi_status,
2958 le32_to_cpu(pReply->IOCLogInfo)));
2959
2960 switch(status) {
2961
2962 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
2963 completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
2964 break;
2965
2966 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
2967 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
2968 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
2969 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
2970 completionCode = MPT_SCANDV_DID_RESET;
2971 break;
2972
2973 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
2974 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
2975 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
2976 if (pReply->Function == MPI_FUNCTION_CONFIG) {
2977 ConfigReply_t *pr = (ConfigReply_t *)mr;
2978 completionCode = MPT_SCANDV_GOOD;
2979 hd->pLocal->header.PageVersion = pr->Header.PageVersion;
2980 hd->pLocal->header.PageLength = pr->Header.PageLength;
2981 hd->pLocal->header.PageNumber = pr->Header.PageNumber;
2982 hd->pLocal->header.PageType = pr->Header.PageType;
2983
2984 } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
2985 /* If the RAID Volume request is successful,
2986 * return GOOD, else indicate that
2987 * some type of error occurred.
2988 */
2989 MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
Christoph Hellwig637fa992005-08-18 16:25:44 +02002990 if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991 completionCode = MPT_SCANDV_GOOD;
2992 else
2993 completionCode = MPT_SCANDV_SOME_ERROR;
James Bottomleyc92f2222006-03-01 09:02:49 -06002994 memcpy(hd->pLocal->sense, pr, sizeof(hd->pLocal->sense));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995
2996 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
2997 u8 *sense_data;
2998 int sz;
2999
3000 /* save sense data in global structure
3001 */
3002 completionCode = MPT_SCANDV_SENSE;
3003 hd->pLocal->scsiStatus = scsi_status;
3004 sense_data = ((u8 *)hd->ioc->sense_buf_pool +
3005 (req_idx * MPT_SENSE_BUFFER_ALLOC));
3006
3007 sz = min_t(int, pReq->SenseBufferLength,
3008 SCSI_STD_SENSE_BYTES);
3009 memcpy(hd->pLocal->sense, sense_data, sz);
3010
3011 ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
3012 sense_data));
3013 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
3014 if (pReq->CDB[0] == INQUIRY)
3015 completionCode = MPT_SCANDV_ISSUE_SENSE;
3016 else
3017 completionCode = MPT_SCANDV_DID_RESET;
3018 }
3019 else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
3020 completionCode = MPT_SCANDV_DID_RESET;
3021 else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3022 completionCode = MPT_SCANDV_DID_RESET;
3023 else {
3024 completionCode = MPT_SCANDV_GOOD;
3025 hd->pLocal->scsiStatus = scsi_status;
3026 }
3027 break;
3028
3029 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
3030 if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3031 completionCode = MPT_SCANDV_DID_RESET;
3032 else
3033 completionCode = MPT_SCANDV_SOME_ERROR;
3034 break;
3035
3036 default:
3037 completionCode = MPT_SCANDV_SOME_ERROR;
3038 break;
3039
3040 } /* switch(status) */
3041
3042 ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n",
3043 completionCode));
3044 } /* end of address reply case */
3045
3046 hd->pLocal->completion = completionCode;
3047
3048 /* MF and RF are freed in mpt_interrupt
3049 */
3050wakeup:
3051 /* Free Chain buffers (will never chain) in scan or dv */
3052 //mptscsih_freeChainBuffers(ioc, req_idx);
3053
3054 /*
3055 * Wake up the original calling thread
3056 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003057 hd->scandv_wait_done = 1;
3058 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003059
3060 return 1;
3061}
3062
3063/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3064/* mptscsih_timer_expired - Call back for timer process.
3065 * Used only for dv functionality.
3066 * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
3067 *
3068 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003069void
3070mptscsih_timer_expired(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003071{
3072 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
3073
3074 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
3075
3076 if (hd->cmdPtr) {
3077 MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
3078
3079 if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
3080 /* Desire to issue a task management request here.
3081 * TM requests MUST be single threaded.
3082 * If old eh code and no TM current, issue request.
3083 * If new eh code, do nothing. Wait for OS cmd timeout
3084 * for bus reset.
3085 */
3086 ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
3087 } else {
3088 /* Perform a FW reload */
3089 if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
3090 printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
3091 }
3092 }
3093 } else {
3094 /* This should NEVER happen */
3095 printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
3096 }
3097
3098 /* No more processing.
3099 * TM call will generate an interrupt for SCSI TM Management.
3100 * The FW will reply to all outstanding commands, callback will finish cleanup.
3101 * Hard reset clean-up will free all resources.
3102 */
3103 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
3104
3105 return;
3106}
3107
Linus Torvalds1da177e2005-04-16 15:20:36 -07003108
3109/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3110/**
3111 * mptscsih_do_cmd - Do internal command.
3112 * @hd: MPT_SCSI_HOST pointer
3113 * @io: INTERNAL_CMD pointer.
3114 *
3115 * Issue the specified internally generated command and do command
3116 * specific cleanup. For bus scan / DV only.
3117 * NOTES: If command is Inquiry and status is good,
3118 * initialize a target structure, save the data
3119 *
3120 * Remark: Single threaded access only.
3121 *
3122 * Return:
3123 * < 0 if an illegal command or no resources
3124 *
3125 * 0 if good
3126 *
3127 * > 0 if command complete but some type of completion error.
3128 */
3129static int
3130mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
3131{
3132 MPT_FRAME_HDR *mf;
3133 SCSIIORequest_t *pScsiReq;
3134 SCSIIORequest_t ReqCopy;
3135 int my_idx, ii, dir;
3136 int rc, cmdTimeout;
3137 int in_isr;
3138 char cmdLen;
3139 char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3140 char cmd = io->cmd;
3141
3142 in_isr = in_interrupt();
3143 if (in_isr) {
3144 dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
3145 hd->ioc->name));
3146 return -EPERM;
3147 }
3148
3149
3150 /* Set command specific information
3151 */
3152 switch (cmd) {
3153 case INQUIRY:
3154 cmdLen = 6;
3155 dir = MPI_SCSIIO_CONTROL_READ;
3156 CDB[0] = cmd;
3157 CDB[4] = io->size;
3158 cmdTimeout = 10;
3159 break;
3160
3161 case TEST_UNIT_READY:
3162 cmdLen = 6;
3163 dir = MPI_SCSIIO_CONTROL_READ;
3164 cmdTimeout = 10;
3165 break;
3166
3167 case START_STOP:
3168 cmdLen = 6;
3169 dir = MPI_SCSIIO_CONTROL_READ;
3170 CDB[0] = cmd;
3171 CDB[4] = 1; /*Spin up the disk */
3172 cmdTimeout = 15;
3173 break;
3174
3175 case REQUEST_SENSE:
3176 cmdLen = 6;
3177 CDB[0] = cmd;
3178 CDB[4] = io->size;
3179 dir = MPI_SCSIIO_CONTROL_READ;
3180 cmdTimeout = 10;
3181 break;
3182
3183 case READ_BUFFER:
3184 cmdLen = 10;
3185 dir = MPI_SCSIIO_CONTROL_READ;
3186 CDB[0] = cmd;
3187 if (io->flags & MPT_ICFLAG_ECHO) {
3188 CDB[1] = 0x0A;
3189 } else {
3190 CDB[1] = 0x02;
3191 }
3192
3193 if (io->flags & MPT_ICFLAG_BUF_CAP) {
3194 CDB[1] |= 0x01;
3195 }
3196 CDB[6] = (io->size >> 16) & 0xFF;
3197 CDB[7] = (io->size >> 8) & 0xFF;
3198 CDB[8] = io->size & 0xFF;
3199 cmdTimeout = 10;
3200 break;
3201
3202 case WRITE_BUFFER:
3203 cmdLen = 10;
3204 dir = MPI_SCSIIO_CONTROL_WRITE;
3205 CDB[0] = cmd;
3206 if (io->flags & MPT_ICFLAG_ECHO) {
3207 CDB[1] = 0x0A;
3208 } else {
3209 CDB[1] = 0x02;
3210 }
3211 CDB[6] = (io->size >> 16) & 0xFF;
3212 CDB[7] = (io->size >> 8) & 0xFF;
3213 CDB[8] = io->size & 0xFF;
3214 cmdTimeout = 10;
3215 break;
3216
3217 case RESERVE:
3218 cmdLen = 6;
3219 dir = MPI_SCSIIO_CONTROL_READ;
3220 CDB[0] = cmd;
3221 cmdTimeout = 10;
3222 break;
3223
3224 case RELEASE:
3225 cmdLen = 6;
3226 dir = MPI_SCSIIO_CONTROL_READ;
3227 CDB[0] = cmd;
3228 cmdTimeout = 10;
3229 break;
3230
3231 case SYNCHRONIZE_CACHE:
3232 cmdLen = 10;
3233 dir = MPI_SCSIIO_CONTROL_READ;
3234 CDB[0] = cmd;
3235// CDB[1] = 0x02; /* set immediate bit */
3236 cmdTimeout = 10;
3237 break;
3238
3239 default:
3240 /* Error Case */
3241 return -EFAULT;
3242 }
3243
3244 /* Get and Populate a free Frame
3245 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003246 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003247 ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
3248 hd->ioc->name));
3249 return -EBUSY;
3250 }
3251
3252 pScsiReq = (SCSIIORequest_t *) mf;
3253
3254 /* Get the request index */
3255 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3256 ADD_INDEX_LOG(my_idx); /* for debug */
3257
3258 if (io->flags & MPT_ICFLAG_PHYS_DISK) {
3259 pScsiReq->TargetID = io->physDiskNum;
3260 pScsiReq->Bus = 0;
3261 pScsiReq->ChainOffset = 0;
3262 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
3263 } else {
3264 pScsiReq->TargetID = io->id;
3265 pScsiReq->Bus = io->bus;
3266 pScsiReq->ChainOffset = 0;
3267 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
3268 }
3269
3270 pScsiReq->CDBLength = cmdLen;
3271 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
3272
3273 pScsiReq->Reserved = 0;
3274
3275 pScsiReq->MsgFlags = mpt_msg_flags();
3276 /* MsgContext set in mpt_get_msg_fram call */
3277
3278 for (ii=0; ii < 8; ii++)
3279 pScsiReq->LUN[ii] = 0;
3280 pScsiReq->LUN[1] = io->lun;
3281
3282 if (io->flags & MPT_ICFLAG_TAGGED_CMD)
3283 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
3284 else
3285 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3286
3287 if (cmd == REQUEST_SENSE) {
3288 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3289 ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n",
3290 hd->ioc->name, cmd));
3291 }
3292
3293 for (ii=0; ii < 16; ii++)
3294 pScsiReq->CDB[ii] = CDB[ii];
3295
3296 pScsiReq->DataLength = cpu_to_le32(io->size);
3297 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
3298 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
3299
3300 ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
3301 hd->ioc->name, cmd, io->bus, io->id, io->lun));
3302
3303 if (dir == MPI_SCSIIO_CONTROL_READ) {
3304 mpt_add_sge((char *) &pScsiReq->SGL,
3305 MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
3306 io->data_dma);
3307 } else {
3308 mpt_add_sge((char *) &pScsiReq->SGL,
3309 MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
3310 io->data_dma);
3311 }
3312
3313 /* The ISR will free the request frame, but we need
3314 * the information to initialize the target. Duplicate.
3315 */
3316 memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
3317
3318 /* Issue this command after:
3319 * finish init
3320 * add timer
3321 * Wait until the reply has been received
3322 * ScsiScanDvCtx callback function will
3323 * set hd->pLocal;
3324 * set scandv_wait_done and call wake_up
3325 */
3326 hd->pLocal = NULL;
3327 hd->timer.expires = jiffies + HZ*cmdTimeout;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003328 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003329
3330 /* Save cmd pointer, for resource free if timeout or
3331 * FW reload occurs
3332 */
3333 hd->cmdPtr = mf;
3334
3335 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003336 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3337 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003338
3339 if (hd->pLocal) {
3340 rc = hd->pLocal->completion;
3341 hd->pLocal->skip = 0;
3342
3343 /* Always set fatal error codes in some cases.
3344 */
3345 if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
3346 rc = -ENXIO;
3347 else if (rc == MPT_SCANDV_SOME_ERROR)
3348 rc = -rc;
3349 } else {
3350 rc = -EFAULT;
3351 /* This should never happen. */
3352 ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
3353 hd->ioc->name));
3354 }
3355
3356 return rc;
3357}
3358
3359/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3360/**
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003361 * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
3362 * @hd: Pointer to a SCSI HOST structure
3363 * @vtarget: per device private data
3364 * @lun: lun
3365 *
3366 * Uses the ISR, but with special processing.
3367 * MUST be single-threaded.
3368 *
3369 */
3370static void
3371mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
3372{
3373 INTERNAL_CMD iocmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003374
3375 /* Following parameters will not change
3376 * in this routine.
3377 */
3378 iocmd.cmd = SYNCHRONIZE_CACHE;
3379 iocmd.flags = 0;
3380 iocmd.physDiskNum = -1;
3381 iocmd.data = NULL;
3382 iocmd.data_dma = -1;
3383 iocmd.size = 0;
3384 iocmd.rsvd = iocmd.rsvd2 = 0;
Moore, Eric914c2d82006-03-14 09:19:36 -07003385 iocmd.bus = vdevice->vtarget->bus_id;
3386 iocmd.id = vdevice->vtarget->target_id;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003387 iocmd.lun = (u8)vdevice->lun;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388
James Bottomleyc92f2222006-03-01 09:02:49 -06003389 if ((vdevice->vtarget->type == TYPE_DISK) &&
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003390 (vdevice->configured_lun))
3391 mptscsih_do_cmd(hd, &iocmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003392}
3393
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003394EXPORT_SYMBOL(mptscsih_remove);
3395EXPORT_SYMBOL(mptscsih_shutdown);
3396#ifdef CONFIG_PM
3397EXPORT_SYMBOL(mptscsih_suspend);
3398EXPORT_SYMBOL(mptscsih_resume);
3399#endif
3400EXPORT_SYMBOL(mptscsih_proc_info);
3401EXPORT_SYMBOL(mptscsih_info);
3402EXPORT_SYMBOL(mptscsih_qcmd);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003403EXPORT_SYMBOL(mptscsih_target_alloc);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003404EXPORT_SYMBOL(mptscsih_slave_alloc);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003405EXPORT_SYMBOL(mptscsih_target_destroy);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003406EXPORT_SYMBOL(mptscsih_slave_destroy);
3407EXPORT_SYMBOL(mptscsih_slave_configure);
3408EXPORT_SYMBOL(mptscsih_abort);
3409EXPORT_SYMBOL(mptscsih_dev_reset);
3410EXPORT_SYMBOL(mptscsih_bus_reset);
3411EXPORT_SYMBOL(mptscsih_host_reset);
3412EXPORT_SYMBOL(mptscsih_bios_param);
3413EXPORT_SYMBOL(mptscsih_io_done);
3414EXPORT_SYMBOL(mptscsih_taskmgmt_complete);
3415EXPORT_SYMBOL(mptscsih_scandv_complete);
3416EXPORT_SYMBOL(mptscsih_event_process);
3417EXPORT_SYMBOL(mptscsih_ioc_reset);
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06003418EXPORT_SYMBOL(mptscsih_change_queue_depth);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003419EXPORT_SYMBOL(mptscsih_timer_expired);
James Bottomley663e1aa2006-01-29 12:10:24 -06003420EXPORT_SYMBOL(mptscsih_TMHandler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003422/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/