blob: ff83e21e100047618778bd1ed75235fc3bea9c85 [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, Eric Deanc7c82982005-11-16 18:54:25 -0700890 if ((mf->TargetID != ((u8)vdevice->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
James Bottomleyc92f2222006-03-01 09:02:49 -06001238 if (vdev->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT &&
1239 mptscsih_raid_id_to_num(hd, SCpnt->device->id) < 0) {
1240 SCpnt->result = DID_NO_CONNECT << 16;
1241 done(SCpnt);
1242 return 0;
1243 }
1244
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 /*
1246 * Put together a MPT SCSI request...
1247 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001248 if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
1250 hd->ioc->name));
1251 return SCSI_MLQUEUE_HOST_BUSY;
1252 }
1253
1254 pScsiReq = (SCSIIORequest_t *) mf;
1255
1256 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
1257
1258 ADD_INDEX_LOG(my_idx);
1259
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001260 /* TUR's being issued with scsictl=0x02000000 (DATA_IN)!
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 * Seems we may receive a buffer (datalen>0) even when there
1262 * will be no data transfer! GRRRRR...
1263 */
1264 if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
1265 datalen = SCpnt->request_bufflen;
1266 scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
1267 } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
1268 datalen = SCpnt->request_bufflen;
1269 scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
1270 } else {
1271 datalen = 0;
1272 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
1273 }
1274
1275 /* Default to untagged. Once a target structure has been allocated,
1276 * use the Inquiry data to determine if device supports tagged.
1277 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001278 if (vdev
1279 && (vdev->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 && (SCpnt->device->tagged_supported)) {
1281 scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
1282 } else {
1283 scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
1284 }
1285
1286 /* Use the above information to set up the message frame
1287 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001288 pScsiReq->TargetID = (u8) vdev->target_id;
1289 pScsiReq->Bus = vdev->bus_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 pScsiReq->ChainOffset = 0;
James Bottomleyc92f2222006-03-01 09:02:49 -06001291 if (vdev->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
1292 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
1293 else
1294 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 pScsiReq->CDBLength = SCpnt->cmd_len;
1296 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
1297 pScsiReq->Reserved = 0;
1298 pScsiReq->MsgFlags = mpt_msg_flags();
1299 pScsiReq->LUN[0] = 0;
1300 pScsiReq->LUN[1] = lun;
1301 pScsiReq->LUN[2] = 0;
1302 pScsiReq->LUN[3] = 0;
1303 pScsiReq->LUN[4] = 0;
1304 pScsiReq->LUN[5] = 0;
1305 pScsiReq->LUN[6] = 0;
1306 pScsiReq->LUN[7] = 0;
1307 pScsiReq->Control = cpu_to_le32(scsictl);
1308
1309 /*
1310 * Write SCSI CDB into the message
1311 */
1312 cmd_len = SCpnt->cmd_len;
1313 for (ii=0; ii < cmd_len; ii++)
1314 pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
1315
1316 for (ii=cmd_len; ii < 16; ii++)
1317 pScsiReq->CDB[ii] = 0;
1318
1319 /* DataLength */
1320 pScsiReq->DataLength = cpu_to_le32(datalen);
1321
1322 /* SenseBuffer low address */
1323 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
1324 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
1325
1326 /* Now add the SG list
1327 * Always have a SGE even if null length.
1328 */
1329 if (datalen == 0) {
1330 /* Add a NULL SGE */
1331 mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
1332 (dma_addr_t) -1);
1333 } else {
1334 /* Add a 32 or 64 bit SGE */
1335 if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS)
1336 goto fail;
1337 }
1338
1339 hd->ScsiLookup[my_idx] = SCpnt;
1340 SCpnt->host_scribble = NULL;
1341
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001342 mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
1344 hd->ioc->name, SCpnt, mf, my_idx));
1345 DBG_DUMP_REQUEST_FRAME(mf)
1346 return 0;
1347
1348 fail:
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001349 hd->ScsiLookup[my_idx] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 mptscsih_freeChainBuffers(hd->ioc, my_idx);
1351 mpt_free_msg_frame(hd->ioc, mf);
1352 return SCSI_MLQUEUE_HOST_BUSY;
1353}
1354
1355/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1356/*
1357 * mptscsih_freeChainBuffers - Function to free chain buffers associated
1358 * with a SCSI IO request
1359 * @hd: Pointer to the MPT_SCSI_HOST instance
1360 * @req_idx: Index of the SCSI IO request frame.
1361 *
1362 * Called if SG chain buffer allocation fails and mptscsih callbacks.
1363 * No return.
1364 */
1365static void
1366mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
1367{
1368 MPT_FRAME_HDR *chain;
1369 unsigned long flags;
1370 int chain_idx;
1371 int next;
1372
1373 /* Get the first chain index and reset
1374 * tracker state.
1375 */
1376 chain_idx = ioc->ReqToChain[req_idx];
1377 ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
1378
1379 while (chain_idx != MPT_HOST_NO_CHAIN) {
1380
1381 /* Save the next chain buffer index */
1382 next = ioc->ChainToChain[chain_idx];
1383
1384 /* Free this chain buffer and reset
1385 * tracker
1386 */
1387 ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
1388
1389 chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer
1390 + (chain_idx * ioc->req_sz));
1391
1392 spin_lock_irqsave(&ioc->FreeQlock, flags);
1393 list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ);
1394 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
1395
1396 dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
1397 ioc->name, chain_idx));
1398
1399 /* handle next */
1400 chain_idx = next;
1401 }
1402 return;
1403}
1404
1405/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1406/*
1407 * Reset Handling
1408 */
1409
1410/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1411/*
1412 * mptscsih_TMHandler - Generic handler for SCSI Task Management.
1413 * Fall through to mpt_HardResetHandler if: not operational, too many
1414 * failed TM requests or handshake failure.
1415 *
1416 * @ioc: Pointer to MPT_ADAPTER structure
1417 * @type: Task Management type
1418 * @target: Logical Target ID for reset (if appropriate)
1419 * @lun: Logical Unit for reset (if appropriate)
1420 * @ctx2abort: Context for the task to be aborted (if appropriate)
1421 *
1422 * Remark: Currently invoked from a non-interrupt thread (_bh).
1423 *
1424 * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
1425 * will be active.
1426 *
1427 * Returns 0 for SUCCESS or -1 if FAILED.
1428 */
James Bottomley663e1aa2006-01-29 12:10:24 -06001429int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1431{
1432 MPT_ADAPTER *ioc;
1433 int rc = -1;
1434 int doTask = 1;
1435 u32 ioc_raw_state;
1436 unsigned long flags;
1437
1438 /* If FW is being reloaded currently, return success to
1439 * the calling function.
1440 */
1441 if (hd == NULL)
1442 return 0;
1443
1444 ioc = hd->ioc;
1445 if (ioc == NULL) {
1446 printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
1447 return FAILED;
1448 }
1449 dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
1450
1451 // SJR - CHECKME - Can we avoid this here?
1452 // (mpt_HardResetHandler has this check...)
1453 spin_lock_irqsave(&ioc->diagLock, flags);
1454 if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
1455 spin_unlock_irqrestore(&ioc->diagLock, flags);
1456 return FAILED;
1457 }
1458 spin_unlock_irqrestore(&ioc->diagLock, flags);
1459
1460 /* Wait a fixed amount of time for the TM pending flag to be cleared.
1461 * If we time out and not bus reset, then we return a FAILED status to the caller.
1462 * The call to mptscsih_tm_pending_wait() will set the pending flag if we are
1463 * successful. Otherwise, reload the FW.
1464 */
1465 if (mptscsih_tm_pending_wait(hd) == FAILED) {
1466 if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001467 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 "Timed out waiting for last TM (%d) to complete! \n",
1469 hd->ioc->name, hd->tmPending));
1470 return FAILED;
1471 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001472 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 "Timed out waiting for last TM (%d) to complete! \n",
1474 hd->ioc->name, hd->tmPending));
1475 return FAILED;
1476 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001477 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 "Timed out waiting for last TM (%d) to complete! \n",
1479 hd->ioc->name, hd->tmPending));
1480 if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
1481 return FAILED;
1482
1483 doTask = 0;
1484 }
1485 } else {
1486 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1487 hd->tmPending |= (1 << type);
1488 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1489 }
1490
1491 /* Is operational?
1492 */
1493 ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
1494
1495#ifdef MPT_DEBUG_RESET
1496 if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
1497 printk(MYIOC_s_WARN_FMT
1498 "TM Handler: IOC Not operational(0x%x)!\n",
1499 hd->ioc->name, ioc_raw_state);
1500 }
1501#endif
1502
1503 if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
1504 && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
1505
1506 /* Isse the Task Mgmt request.
1507 */
1508 if (hd->hard_resets < -1)
1509 hd->hard_resets++;
1510 rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout);
1511 if (rc) {
1512 printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
1513 } else {
1514 dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
1515 }
1516 }
1517
1518 /* Only fall through to the HRH if this is a bus reset
1519 */
1520 if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
1521 ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
1522 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1523 hd->ioc->name));
1524 rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1525 }
1526
1527 dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
1528
1529 return rc;
1530}
1531
1532
1533/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1534/*
1535 * mptscsih_IssueTaskMgmt - Generic send Task Management function.
1536 * @hd: Pointer to MPT_SCSI_HOST structure
1537 * @type: Task Management type
1538 * @target: Logical Target ID for reset (if appropriate)
1539 * @lun: Logical Unit for reset (if appropriate)
1540 * @ctx2abort: Context for the task to be aborted (if appropriate)
1541 *
1542 * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
1543 * or a non-interrupt thread. In the former, must not call schedule().
1544 *
1545 * Not all fields are meaningfull for all task types.
1546 *
1547 * Returns 0 for SUCCESS, -999 for "no msg frames",
1548 * else other non-zero value returned.
1549 */
1550static int
1551mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1552{
1553 MPT_FRAME_HDR *mf;
1554 SCSITaskMgmt_t *pScsiTm;
1555 int ii;
1556 int retval;
1557
1558 /* Return Fail to calling function if no message frames available.
1559 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001560 if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
1562 hd->ioc->name));
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001563 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 }
1565 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
1566 hd->ioc->name, mf));
1567
1568 /* Format the Request
1569 */
1570 pScsiTm = (SCSITaskMgmt_t *) mf;
1571 pScsiTm->TargetID = target;
1572 pScsiTm->Bus = channel;
1573 pScsiTm->ChainOffset = 0;
1574 pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1575
1576 pScsiTm->Reserved = 0;
1577 pScsiTm->TaskType = type;
1578 pScsiTm->Reserved1 = 0;
1579 pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
1580 ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
1581
1582 for (ii= 0; ii < 8; ii++) {
1583 pScsiTm->LUN[ii] = 0;
1584 }
1585 pScsiTm->LUN[1] = lun;
1586
1587 for (ii=0; ii < 7; ii++)
1588 pScsiTm->Reserved2[ii] = 0;
1589
1590 pScsiTm->TaskMsgContext = ctx2abort;
1591
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001592 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n",
1593 hd->ioc->name, ctx2abort, type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
1595 DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm);
1596
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001597 if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 sizeof(SCSITaskMgmt_t), (u32*)pScsiTm,
1599 CAN_SLEEP)) != 0) {
1600 dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!"
1601 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1602 hd->ioc, mf));
1603 mpt_free_msg_frame(hd->ioc, mf);
1604 return retval;
1605 }
1606
1607 if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
1608 dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!"
1609 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1610 hd->ioc, mf));
1611 mpt_free_msg_frame(hd->ioc, mf);
1612 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1613 hd->ioc->name));
1614 retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1615 }
1616
1617 return retval;
1618}
1619
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001620static int
1621mptscsih_get_tm_timeout(MPT_ADAPTER *ioc)
1622{
1623 switch (ioc->bus_type) {
1624 case FC:
1625 return 40;
1626 case SAS:
1627 return 10;
1628 case SPI:
1629 default:
1630 return 2;
1631 }
1632}
1633
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1635/**
1636 * mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant
1637 * @SCpnt: Pointer to scsi_cmnd structure, IO to be aborted
1638 *
1639 * (linux scsi_host_template.eh_abort_handler routine)
1640 *
1641 * Returns SUCCESS or FAILED.
1642 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001643int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644mptscsih_abort(struct scsi_cmnd * SCpnt)
1645{
1646 MPT_SCSI_HOST *hd;
1647 MPT_ADAPTER *ioc;
1648 MPT_FRAME_HDR *mf;
1649 u32 ctx2abort;
1650 int scpnt_idx;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001651 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001652 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654 /* If we can't locate our host adapter structure, return FAILED status.
1655 */
1656 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) {
1657 SCpnt->result = DID_RESET << 16;
1658 SCpnt->scsi_done(SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001659 dfailprintk((KERN_INFO MYNAM ": mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 "Can't locate host! (sc=%p)\n",
1661 SCpnt));
1662 return FAILED;
1663 }
1664
1665 ioc = hd->ioc;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001666 if (hd->resetPending) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 return FAILED;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001668 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 if (hd->timeouts < -1)
1671 hd->timeouts++;
1672
1673 /* Find this command
1674 */
1675 if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001676 /* Cmd not found in ScsiLookup.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 * Do OS callback.
1678 */
1679 SCpnt->result = DID_RESET << 16;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001680 dtmprintk((KERN_INFO MYNAM ": %s: mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 "Command not in the active list! (sc=%p)\n",
1682 hd->ioc->name, SCpnt));
1683 return SUCCESS;
1684 }
1685
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001686 printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n",
1687 hd->ioc->name, SCpnt);
1688 scsi_print_command(SCpnt);
1689
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
1691 * (the IO to be ABORT'd)
1692 *
1693 * NOTE: Since we do not byteswap MsgContext, we do not
1694 * swap it here either. It is an opaque cookie to
1695 * the controller, so it does not matter. -DaveM
1696 */
1697 mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
1698 ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
1699
1700 hd->abortSCpnt = SCpnt;
1701
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001702 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001703 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001704 vdev->bus_id, vdev->target_id, vdev->lun,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001705 ctx2abort, mptscsih_get_tm_timeout(ioc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001707 printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n",
1708 hd->ioc->name,
1709 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001711 if (retval == 0)
1712 return SUCCESS;
1713
1714 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 hd->tmPending = 0;
1716 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001718 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719}
1720
1721/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1722/**
1723 * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
1724 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1725 *
1726 * (linux scsi_host_template.eh_dev_reset_handler routine)
1727 *
1728 * Returns SUCCESS or FAILED.
1729 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001730int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
1732{
1733 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001734 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001735 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736
1737 /* If we can't locate our host adapter structure, return FAILED status.
1738 */
1739 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001740 dtmprintk((KERN_INFO MYNAM ": mptscsih_dev_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 "Can't locate host! (sc=%p)\n",
1742 SCpnt));
1743 return FAILED;
1744 }
1745
1746 if (hd->resetPending)
1747 return FAILED;
1748
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001749 printk(KERN_WARNING MYNAM ": %s: attempting target reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001751 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001753 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001754 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001755 vdev->bus_id, vdev->target_id,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001756 0, 0, mptscsih_get_tm_timeout(hd->ioc));
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001757
1758 printk (KERN_WARNING MYNAM ": %s: target reset: %s (sc=%p)\n",
1759 hd->ioc->name,
1760 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1761
1762 if (retval == 0)
1763 return SUCCESS;
1764
1765 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 hd->tmPending = 0;
1767 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001769 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770}
1771
1772/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1773/**
1774 * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
1775 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1776 *
1777 * (linux scsi_host_template.eh_bus_reset_handler routine)
1778 *
1779 * Returns SUCCESS or FAILED.
1780 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001781int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
1783{
1784 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001785 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001786 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
1788 /* If we can't locate our host adapter structure, return FAILED status.
1789 */
1790 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001791 dtmprintk((KERN_INFO MYNAM ": mptscsih_bus_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 "Can't locate host! (sc=%p)\n",
1793 SCpnt ) );
1794 return FAILED;
1795 }
1796
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001797 printk(KERN_WARNING MYNAM ": %s: attempting bus reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001799 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
1801 if (hd->timeouts < -1)
1802 hd->timeouts++;
1803
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001804 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001805 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001806 vdev->bus_id, 0, 0, 0, mptscsih_get_tm_timeout(hd->ioc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001808 printk (KERN_WARNING MYNAM ": %s: bus reset: %s (sc=%p)\n",
1809 hd->ioc->name,
1810 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1811
1812 if (retval == 0)
1813 return SUCCESS;
1814
1815 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 hd->tmPending = 0;
1817 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001819 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820}
1821
1822/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1823/**
1824 * mptscsih_host_reset - Perform a SCSI host adapter RESET!
1825 * new_eh variant
1826 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1827 *
1828 * (linux scsi_host_template.eh_host_reset_handler routine)
1829 *
1830 * Returns SUCCESS or FAILED.
1831 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001832int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833mptscsih_host_reset(struct scsi_cmnd *SCpnt)
1834{
1835 MPT_SCSI_HOST * hd;
1836 int status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
1838 /* If we can't locate the host to reset, then we failed. */
1839 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001840 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841 "Can't locate host! (sc=%p)\n",
1842 SCpnt ) );
1843 return FAILED;
1844 }
1845
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001846 printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 hd->ioc->name, SCpnt);
1848
1849 /* If our attempts to reset the host failed, then return a failed
1850 * status. The host will be taken off line by the SCSI mid-layer.
1851 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
1853 status = FAILED;
1854 } else {
1855 /* Make sure TM pending is cleared and TM state is set to
1856 * NONE.
1857 */
1858 hd->tmPending = 0;
1859 hd->tmState = TM_STATE_NONE;
1860 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001862 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 "Status = %s\n",
1864 (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
1865
1866 return status;
1867}
1868
1869/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1870/**
1871 * mptscsih_tm_pending_wait - wait for pending task management request to
1872 * complete.
1873 * @hd: Pointer to MPT host structure.
1874 *
1875 * Returns {SUCCESS,FAILED}.
1876 */
1877static int
1878mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
1879{
1880 unsigned long flags;
1881 int loop_count = 4 * 10; /* Wait 10 seconds */
1882 int status = FAILED;
1883
1884 do {
1885 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1886 if (hd->tmState == TM_STATE_NONE) {
1887 hd->tmState = TM_STATE_IN_PROGRESS;
1888 hd->tmPending = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001890 status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 break;
1892 }
1893 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1894 msleep(250);
1895 } while (--loop_count);
1896
1897 return status;
1898}
1899
1900/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1901/**
1902 * mptscsih_tm_wait_for_completion - wait for completion of TM task
1903 * @hd: Pointer to MPT host structure.
1904 *
1905 * Returns {SUCCESS,FAILED}.
1906 */
1907static int
1908mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
1909{
1910 unsigned long flags;
1911 int loop_count = 4 * timeout;
1912 int status = FAILED;
1913
1914 do {
1915 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1916 if(hd->tmPending == 0) {
1917 status = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001918 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 break;
1920 }
1921 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1922 msleep_interruptible(250);
1923 } while (--loop_count);
1924
1925 return status;
1926}
1927
1928/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric9f63bb72006-01-16 18:53:26 -07001929static void
1930mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code)
1931{
1932 char *desc;
1933
1934 switch (response_code) {
1935 case MPI_SCSITASKMGMT_RSP_TM_COMPLETE:
1936 desc = "The task completed.";
1937 break;
1938 case MPI_SCSITASKMGMT_RSP_INVALID_FRAME:
1939 desc = "The IOC received an invalid frame status.";
1940 break;
1941 case MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
1942 desc = "The task type is not supported.";
1943 break;
1944 case MPI_SCSITASKMGMT_RSP_TM_FAILED:
1945 desc = "The requested task failed.";
1946 break;
1947 case MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED:
1948 desc = "The task completed successfully.";
1949 break;
1950 case MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN:
1951 desc = "The LUN request is invalid.";
1952 break;
1953 case MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
1954 desc = "The task is in the IOC queue and has not been sent to target.";
1955 break;
1956 default:
1957 desc = "unknown";
1958 break;
1959 }
1960 printk(MYIOC_s_INFO_FMT "Response Code(0x%08x): F/W: %s\n",
1961 ioc->name, response_code, desc);
1962}
1963
1964/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965/**
1966 * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
1967 * @ioc: Pointer to MPT_ADAPTER structure
1968 * @mf: Pointer to SCSI task mgmt request frame
1969 * @mr: Pointer to SCSI task mgmt reply frame
1970 *
1971 * This routine is called from mptbase.c::mpt_interrupt() at the completion
1972 * of any SCSI task management request.
1973 * This routine is registered with the MPT (base) driver at driver
1974 * load/init time via the mpt_register() API call.
1975 *
1976 * Returns 1 indicating alloc'd request frame ptr should be freed.
1977 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001978int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
1980{
1981 SCSITaskMgmtReply_t *pScsiTmReply;
1982 SCSITaskMgmt_t *pScsiTmReq;
1983 MPT_SCSI_HOST *hd;
1984 unsigned long flags;
1985 u16 iocstatus;
1986 u8 tmType;
1987
1988 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
1989 ioc->name, mf, mr));
1990 if (ioc->sh) {
1991 /* Depending on the thread, a timer is activated for
1992 * the TM request. Delete this timer on completion of TM.
1993 * Decrement count of outstanding TM requests.
1994 */
1995 hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
1996 } else {
1997 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
1998 ioc->name));
1999 return 1;
2000 }
2001
2002 if (mr == NULL) {
2003 dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
2004 ioc->name, mf));
2005 return 1;
2006 } else {
2007 pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
2008 pScsiTmReq = (SCSITaskMgmt_t*)mf;
2009
2010 /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
2011 tmType = pScsiTmReq->TaskType;
2012
Moore, Eric9f63bb72006-01-16 18:53:26 -07002013 if (ioc->facts.MsgVersion >= MPI_VERSION_01_05 &&
2014 pScsiTmReply->ResponseCode)
2015 mptscsih_taskmgmt_response_code(ioc,
2016 pScsiTmReply->ResponseCode);
2017
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n",
2019 ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
2020 DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply);
2021
2022 iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2023 dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n",
2024 ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo)));
2025 /* Error? (anything non-zero?) */
2026 if (iocstatus) {
2027
2028 /* clear flags and continue.
2029 */
2030 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
2031 hd->abortSCpnt = NULL;
2032
2033 /* If an internal command is present
2034 * or the TM failed - reload the FW.
2035 * FC FW may respond FAILED to an ABORT
2036 */
2037 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
2038 if ((hd->cmdPtr) ||
2039 (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
2040 if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
2041 printk((KERN_WARNING
2042 " Firmware Reload FAILED!!\n"));
2043 }
2044 }
2045 }
2046 } else {
2047 dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name));
2048
2049 hd->abortSCpnt = NULL;
2050
2051 }
2052 }
2053
2054 spin_lock_irqsave(&ioc->FreeQlock, flags);
2055 hd->tmPending = 0;
2056 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2057 hd->tmState = TM_STATE_NONE;
2058
2059 return 1;
2060}
2061
2062/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2063/*
2064 * This is anyones guess quite frankly.
2065 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002066int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
2068 sector_t capacity, int geom[])
2069{
2070 int heads;
2071 int sectors;
2072 sector_t cylinders;
2073 ulong dummy;
2074
2075 heads = 64;
2076 sectors = 32;
2077
2078 dummy = heads * sectors;
2079 cylinders = capacity;
2080 sector_div(cylinders,dummy);
2081
2082 /*
2083 * Handle extended translation size for logical drives
2084 * > 1Gb
2085 */
2086 if ((ulong)capacity >= 0x200000) {
2087 heads = 255;
2088 sectors = 63;
2089 dummy = heads * sectors;
2090 cylinders = capacity;
2091 sector_div(cylinders,dummy);
2092 }
2093
2094 /* return result */
2095 geom[0] = heads;
2096 geom[1] = sectors;
2097 geom[2] = cylinders;
2098
2099 dprintk((KERN_NOTICE
2100 ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n",
2101 sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors));
2102
2103 return 0;
2104}
2105
James Bottomleyc92f2222006-03-01 09:02:49 -06002106int
2107mptscsih_raid_id_to_num(MPT_SCSI_HOST *hd, uint physdiskid)
2108{
2109 int i;
2110
2111 if (!hd->ioc->raid_data.isRaid || !hd->ioc->raid_data.pIocPg3)
2112 return -ENXIO;
2113
2114 for (i = 0; i < hd->ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
2115 if (physdiskid ==
2116 hd->ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID)
2117 return hd->ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum;
2118 }
2119
2120 return -ENXIO;
2121}
2122EXPORT_SYMBOL(mptscsih_raid_id_to_num);
2123
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2125/*
2126 * OS entry point to allow host driver to alloc memory
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002127 * for each scsi target. Called once per device the bus scan.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 * Return non-zero if allocation fails.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002130int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002131mptscsih_target_alloc(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002133 VirtTarget *vtarget;
2134
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +01002135 vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002136 if (!vtarget)
2137 return -ENOMEM;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002138 starget->hostdata = vtarget;
James Bottomleyc92f2222006-03-01 09:02:49 -06002139 vtarget->starget = starget;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002140 return 0;
2141}
2142
2143/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2144/*
2145 * OS entry point to allow host driver to alloc memory
2146 * for each scsi device. Called once per device the bus scan.
2147 * Return non-zero if allocation fails.
2148 */
2149int
2150mptscsih_slave_alloc(struct scsi_device *sdev)
2151{
2152 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002154 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 VirtDevice *vdev;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002156 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +01002158 vdev = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 if (!vdev) {
2160 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
2161 hd->ioc->name, sizeof(VirtDevice));
2162 return -ENOMEM;
2163 }
2164
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 vdev->ioc_id = hd->ioc->id;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002166 vdev->target_id = sdev->id;
2167 vdev->bus_id = sdev->channel;
2168 vdev->lun = sdev->lun;
2169 sdev->hostdata = vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002171 starget = scsi_target(sdev);
2172 vtarget = starget->hostdata;
James Bottomleyc92f2222006-03-01 09:02:49 -06002173
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002174 vdev->vtarget = vtarget;
2175
2176 if (vtarget->num_luns == 0) {
2177 hd->Targets[sdev->id] = vtarget;
2178 vtarget->ioc_id = hd->ioc->id;
2179 vtarget->tflags = MPT_TARGET_FLAGS_Q_YES;
2180 vtarget->target_id = sdev->id;
2181 vtarget->bus_id = sdev->channel;
James Bottomleyc92f2222006-03-01 09:02:49 -06002182 if (hd->ioc->bus_type == SPI && sdev->channel == 0 &&
2183 hd->ioc->raid_data.isRaid & (1 << sdev->id)) {
2184 vtarget->raidVolume = 1;
2185 ddvtprintk((KERN_INFO
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002186 "RAID Volume @ id %d\n", sdev->id));
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002187 }
2188 }
2189 vtarget->num_luns++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 return 0;
2191}
2192
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193/*
2194 * OS entry point to allow for host driver to free allocated memory
2195 * Called if no device present or device being unloaded
2196 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002197void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002198mptscsih_target_destroy(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002200 if (starget->hostdata)
2201 kfree(starget->hostdata);
2202 starget->hostdata = NULL;
2203}
2204
2205/*
2206 * OS entry point to allow for host driver to free allocated memory
2207 * Called if no device present or device being unloaded
2208 */
2209void
2210mptscsih_slave_destroy(struct scsi_device *sdev)
2211{
2212 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002214 VirtTarget *vtarget;
2215 VirtDevice *vdevice;
2216 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002218 starget = scsi_target(sdev);
2219 vtarget = starget->hostdata;
2220 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002222 mptscsih_search_running_cmds(hd, vdevice);
2223 vtarget->luns[0] &= ~(1 << vdevice->lun);
2224 vtarget->num_luns--;
2225 if (vtarget->num_luns == 0) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002226 hd->Targets[sdev->id] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002228 mptscsih_synchronize_cache(hd, vdevice);
2229 kfree(vdevice);
2230 sdev->hostdata = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231}
2232
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002233/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2234/*
2235 * mptscsih_change_queue_depth - This function will set a devices queue depth
2236 * @sdev: per scsi_device pointer
2237 * @qdepth: requested queue depth
2238 *
2239 * Adding support for new 'change_queue_depth' api.
2240*/
2241int
2242mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002244 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
2245 VirtTarget *vtarget;
2246 struct scsi_target *starget;
2247 int max_depth;
2248 int tagged;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002250 starget = scsi_target(sdev);
2251 vtarget = starget->hostdata;
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002252
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002253 if (hd->ioc->bus_type == SPI) {
James Bottomleyc92f2222006-03-01 09:02:49 -06002254 if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 max_depth = 1;
James Bottomleyc92f2222006-03-01 09:02:49 -06002256 else if (sdev->type == TYPE_DISK &&
2257 vtarget->minSyncFactor <= MPT_ULTRA160)
2258 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2259 else
2260 max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 } else
2262 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2263
2264 if (qdepth > max_depth)
2265 qdepth = max_depth;
2266 if (qdepth == 1)
2267 tagged = 0;
2268 else
2269 tagged = MSG_SIMPLE_TAG;
2270
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002271 scsi_adjust_queue_depth(sdev, tagged, qdepth);
2272 return sdev->queue_depth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273}
2274
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275/*
2276 * OS entry point to adjust the queue_depths on a per-device basis.
2277 * Called once per device the bus scan. Use it to force the queue_depth
2278 * member to 1 if a device does not support Q tags.
2279 * Return non-zero if fails.
2280 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002281int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002282mptscsih_slave_configure(struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002284 struct Scsi_Host *sh = sdev->host;
2285 VirtTarget *vtarget;
2286 VirtDevice *vdevice;
2287 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sh->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002289 int indexed_lun, lun_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002291 starget = scsi_target(sdev);
2292 vtarget = starget->hostdata;
2293 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294
2295 dsprintk((MYIOC_s_INFO_FMT
2296 "device @ %p, id=%d, LUN=%d, channel=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002297 hd->ioc->name, sdev, sdev->id, sdev->lun, sdev->channel));
2298 if (hd->ioc->bus_type == SPI)
2299 dsprintk((MYIOC_s_INFO_FMT
2300 "sdtr %d wdtr %d ppr %d inq length=%d\n",
2301 hd->ioc->name, sdev->sdtr, sdev->wdtr,
2302 sdev->ppr, sdev->inquiry_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002304 if (sdev->id > sh->max_id) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 /* error case, should never happen */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002306 scsi_adjust_queue_depth(sdev, 0, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 goto slave_configure_exit;
2308 }
2309
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002310 vdevice->configured_lun=1;
2311 lun_index = (vdevice->lun >> 5); /* 32 luns per lun_index */
2312 indexed_lun = (vdevice->lun % 32);
2313 vtarget->luns[lun_index] |= (1 << indexed_lun);
James Bottomleyc92f2222006-03-01 09:02:49 -06002314 mptscsih_initTarget(hd, vtarget, sdev);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002315 mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316
2317 dsprintk((MYIOC_s_INFO_FMT
2318 "Queue depth=%d, tflags=%x\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002319 hd->ioc->name, sdev->queue_depth, vtarget->tflags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002321 if (hd->ioc->bus_type == SPI)
2322 dsprintk((MYIOC_s_INFO_FMT
2323 "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n",
2324 hd->ioc->name, vtarget->negoFlags, vtarget->maxOffset,
2325 vtarget->minSyncFactor));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326
2327slave_configure_exit:
2328
2329 dsprintk((MYIOC_s_INFO_FMT
2330 "tagged %d, simple %d, ordered %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002331 hd->ioc->name,sdev->tagged_supported, sdev->simple_tags,
2332 sdev->ordered_tags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333
2334 return 0;
2335}
2336
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2338/*
2339 * Private routines...
2340 */
2341
2342/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2343/* Utility function to copy sense data from the scsi_cmnd buffer
2344 * to the FC and SCSI target structures.
2345 *
2346 */
2347static void
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002348mptscsih_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 -07002349{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002350 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 SCSIIORequest_t *pReq;
2352 u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353
2354 /* Get target structure
2355 */
2356 pReq = (SCSIIORequest_t *) mf;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002357 vdev = sc->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358
2359 if (sense_count) {
2360 u8 *sense_data;
2361 int req_index;
2362
2363 /* Copy the sense received into the scsi command block. */
2364 req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2365 sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
2366 memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
2367
2368 /* Log SMART data (asc = 0x5D, non-IM case only) if required.
2369 */
2370 if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002371 if ((sense_data[12] == 0x5D) && (vdev->vtarget->raidVolume == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372 int idx;
2373 MPT_ADAPTER *ioc = hd->ioc;
2374
Moore, Eric5b5ef4f2006-02-02 17:19:40 -07002375 idx = ioc->eventContext % MPTCTL_EVENT_LOG_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
2377 ioc->events[idx].eventContext = ioc->eventContext;
2378
2379 ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
2380 (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002381 (sc->device->channel << 8) || sc->device->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
2383 ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
2384
2385 ioc->eventContext++;
2386 }
2387 }
2388 } else {
2389 dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
2390 hd->ioc->name));
2391 }
2392}
2393
2394static u32
2395SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
2396{
2397 MPT_SCSI_HOST *hd;
2398 int i;
2399
2400 hd = (MPT_SCSI_HOST *) sc->device->host->hostdata;
2401
2402 for (i = 0; i < hd->ioc->req_depth; i++) {
2403 if (hd->ScsiLookup[i] == sc) {
2404 return i;
2405 }
2406 }
2407
2408 return -1;
2409}
2410
2411/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002412int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
2414{
2415 MPT_SCSI_HOST *hd;
2416 unsigned long flags;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002417 int ii;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418
2419 dtmprintk((KERN_WARNING MYNAM
2420 ": IOC %s_reset routed to SCSI host driver!\n",
2421 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
2422 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
2423
2424 /* If a FW reload request arrives after base installed but
2425 * before all scsi hosts have been attached, then an alt_ioc
2426 * may have a NULL sh pointer.
2427 */
2428 if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
2429 return 0;
2430 else
2431 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2432
2433 if (reset_phase == MPT_IOC_SETUP_RESET) {
2434 dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
2435
2436 /* Clean Up:
2437 * 1. Set Hard Reset Pending Flag
2438 * All new commands go to doneQ
2439 */
2440 hd->resetPending = 1;
2441
2442 } else if (reset_phase == MPT_IOC_PRE_RESET) {
2443 dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
2444
2445 /* 2. Flush running commands
2446 * Clean ScsiLookup (and associated memory)
2447 * AND clean mytaskQ
2448 */
2449
2450 /* 2b. Reply to OS all known outstanding I/O commands.
2451 */
2452 mptscsih_flush_running_cmds(hd);
2453
2454 /* 2c. If there was an internal command that
2455 * has not completed, configuration or io request,
2456 * free these resources.
2457 */
2458 if (hd->cmdPtr) {
2459 del_timer(&hd->timer);
2460 mpt_free_msg_frame(ioc, hd->cmdPtr);
2461 }
2462
2463 dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
2464
2465 } else {
2466 dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
2467
2468 /* Once a FW reload begins, all new OS commands are
2469 * redirected to the doneQ w/ a reset status.
2470 * Init all control structures.
2471 */
2472
2473 /* ScsiLookup initialization
2474 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002475 for (ii=0; ii < hd->ioc->req_depth; ii++)
2476 hd->ScsiLookup[ii] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477
2478 /* 2. Chain Buffer initialization
2479 */
2480
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002481 /* 4. Renegotiate to all devices, if SPI
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483
2484 /* 5. Enable new commands to be posted
2485 */
2486 spin_lock_irqsave(&ioc->FreeQlock, flags);
2487 hd->tmPending = 0;
2488 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2489 hd->resetPending = 0;
2490 hd->tmState = TM_STATE_NONE;
2491
2492 /* 6. If there was an internal command,
2493 * wake this process up.
2494 */
2495 if (hd->cmdPtr) {
2496 /*
2497 * Wake up the original calling thread
2498 */
2499 hd->pLocal = &hd->localReply;
2500 hd->pLocal->completion = MPT_SCANDV_DID_RESET;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002501 hd->scandv_wait_done = 1;
2502 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 hd->cmdPtr = NULL;
2504 }
2505
Michael Reed05e8ec12006-01-13 14:31:54 -06002506 /* 7. FC: Rescan for blocked rports which might have returned.
2507 */
2508 else if (ioc->bus_type == FC) {
2509 int work_count;
2510 unsigned long flags;
2511
2512 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
2513 work_count = ++ioc->fc_rescan_work_count;
2514 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
2515 if (work_count == 1)
2516 schedule_work(&ioc->fc_rescan_work);
2517 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
2519
2520 }
2521
2522 return 1; /* currently means nothing really */
2523}
2524
2525/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002526int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
2528{
2529 MPT_SCSI_HOST *hd;
2530 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
Michael Reed05e8ec12006-01-13 14:31:54 -06002531 int work_count;
2532 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533
2534 devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
2535 ioc->name, event));
2536
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002537 if (ioc->sh == NULL ||
2538 ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL))
2539 return 1;
2540
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 switch (event) {
2542 case MPI_EVENT_UNIT_ATTENTION: /* 03 */
2543 /* FIXME! */
2544 break;
2545 case MPI_EVENT_IOC_BUS_RESET: /* 04 */
2546 case MPI_EVENT_EXT_BUS_RESET: /* 05 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002547 if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1))
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002548 hd->soft_resets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 break;
2550 case MPI_EVENT_LOGOUT: /* 09 */
2551 /* FIXME! */
2552 break;
2553
Michael Reed05e8ec12006-01-13 14:31:54 -06002554 case MPI_EVENT_RESCAN: /* 06 */
2555 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
2556 work_count = ++ioc->fc_rescan_work_count;
2557 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
2558 if (work_count == 1)
2559 schedule_work(&ioc->fc_rescan_work);
2560 break;
2561
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 /*
2563 * CHECKME! Don't think we need to do
2564 * anything for these, but...
2565 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
2567 case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
2568 /*
2569 * CHECKME! Falling thru...
2570 */
2571 break;
2572
2573 case MPI_EVENT_INTEGRATED_RAID: /* 0B */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002574 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 case MPI_EVENT_NONE: /* 00 */
2577 case MPI_EVENT_LOG_DATA: /* 01 */
2578 case MPI_EVENT_STATE_CHANGE: /* 02 */
2579 case MPI_EVENT_EVENT_CHANGE: /* 0A */
2580 default:
2581 dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
2582 break;
2583 }
2584
2585 return 1; /* currently means nothing really */
2586}
2587
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2589/*
2590 * mptscsih_initTarget - Target, LUN alloc/free functionality.
2591 * @hd: Pointer to MPT_SCSI_HOST structure
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002592 * @vtarget: per target private data
James Bottomleyc92f2222006-03-01 09:02:49 -06002593 * @sdev: SCSI device
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 *
2595 * NOTE: It's only SAFE to call this routine if data points to
2596 * sane & valid STANDARD INQUIRY data!
2597 *
2598 * Allocate and initialize memory for this target.
2599 * Save inquiry data.
2600 *
2601 */
2602static void
James Bottomleyc92f2222006-03-01 09:02:49 -06002603mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget,
2604 struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606 dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002607 hd->ioc->name, vtarget->bus_id, vtarget->target_id, lun, hd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609 /* Is LUN supported? If so, upper 2 bits will be 0
2610 * in first byte of inquiry data.
2611 */
James Bottomleyc92f2222006-03-01 09:02:49 -06002612 if (sdev->inq_periph_qual != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 return;
2614
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002615 if (vtarget == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617
James Bottomleyc92f2222006-03-01 09:02:49 -06002618 vtarget->type = sdev->type;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002620 if (hd->ioc->bus_type != SPI)
2621 return;
2622
James Bottomleyc92f2222006-03-01 09:02:49 -06002623 if ((sdev->type == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002624 /* Treat all Processors as SAF-TE if
2625 * command line option is set */
2626 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2627 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
James Bottomleyc92f2222006-03-01 09:02:49 -06002628 }else if ((sdev->type == TYPE_PROCESSOR) &&
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002629 !(vtarget->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) {
James Bottomleyc92f2222006-03-01 09:02:49 -06002630 if (sdev->inquiry_len > 49 ) {
2631 if (sdev->inquiry[44] == 'S' &&
2632 sdev->inquiry[45] == 'A' &&
2633 sdev->inquiry[46] == 'F' &&
2634 sdev->inquiry[47] == '-' &&
2635 sdev->inquiry[48] == 'T' &&
2636 sdev->inquiry[49] == 'E' ) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002637 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2638 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639 }
2640 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002641 }
James Bottomleyc92f2222006-03-01 09:02:49 -06002642 mptscsih_setTargetNegoParms(hd, vtarget, sdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643}
2644
2645/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2646/*
2647 * Update the target negotiation parameters based on the
2648 * the Inquiry data, adapter capabilities, and NVRAM settings.
2649 *
2650 */
2651static void
James Bottomleyc92f2222006-03-01 09:02:49 -06002652mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *target,
2653 struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654{
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002655 SpiCfgData *pspi_data = &hd->ioc->spi_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 int id = (int) target->target_id;
2657 int nvram;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658 u8 width = MPT_NARROW;
2659 u8 factor = MPT_ASYNC;
2660 u8 offset = 0;
James Bottomleyc92f2222006-03-01 09:02:49 -06002661 u8 nfactor;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 u8 noQas = 1;
2663
2664 target->negoFlags = pspi_data->noQas;
2665
James Bottomleyc92f2222006-03-01 09:02:49 -06002666 /* noQas == 0 => device supports QAS. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
James Bottomleyc92f2222006-03-01 09:02:49 -06002668 if (sdev->scsi_level < SCSI_2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 width = 0;
2670 factor = MPT_ULTRA2;
2671 offset = pspi_data->maxSyncOffset;
2672 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2673 } else {
James Bottomleyc92f2222006-03-01 09:02:49 -06002674 if (scsi_device_wide(sdev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 width = 1;
2676 }
2677
James Bottomleyc92f2222006-03-01 09:02:49 -06002678 if (scsi_device_sync(sdev)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 factor = pspi_data->minSyncFactor;
James Bottomleyc92f2222006-03-01 09:02:49 -06002680 if (!scsi_device_dt(sdev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 factor = MPT_ULTRA2;
James Bottomleyc92f2222006-03-01 09:02:49 -06002682 else {
2683 if (!scsi_device_ius(sdev) &&
2684 !scsi_device_qas(sdev))
2685 factor = MPT_ULTRA160;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 else {
James Bottomleyc92f2222006-03-01 09:02:49 -06002687 factor = MPT_ULTRA320;
2688 if (scsi_device_qas(sdev)) {
2689 ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id));
2690 noQas = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691 }
James Bottomleyc92f2222006-03-01 09:02:49 -06002692 if (sdev->type == TYPE_TAPE &&
2693 scsi_device_ius(sdev))
2694 target->negoFlags |= MPT_TAPE_NEGO_IDP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697 offset = pspi_data->maxSyncOffset;
2698
2699 /* If RAID, never disable QAS
2700 * else if non RAID, do not disable
2701 * QAS if bit 1 is set
2702 * bit 1 QAS support, non-raid only
2703 * bit 0 IU support
2704 */
2705 if (target->raidVolume == 1) {
2706 noQas = 0;
2707 }
2708 } else {
2709 factor = MPT_ASYNC;
2710 offset = 0;
2711 }
2712 }
2713
James Bottomleyc92f2222006-03-01 09:02:49 -06002714 if (!sdev->tagged_supported) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2716 }
2717
2718 /* Update tflags based on NVRAM settings. (SCSI only)
2719 */
2720 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
2721 nvram = pspi_data->nvram[id];
2722 nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
2723
2724 if (width)
2725 width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
2726
2727 if (offset > 0) {
2728 /* Ensure factor is set to the
2729 * maximum of: adapter, nvram, inquiry
2730 */
2731 if (nfactor) {
2732 if (nfactor < pspi_data->minSyncFactor )
2733 nfactor = pspi_data->minSyncFactor;
2734
2735 factor = max(factor, nfactor);
2736 if (factor == MPT_ASYNC)
2737 offset = 0;
2738 } else {
2739 offset = 0;
2740 factor = MPT_ASYNC;
2741 }
2742 } else {
2743 factor = MPT_ASYNC;
2744 }
2745 }
2746
2747 /* Make sure data is consistent
2748 */
2749 if ((!width) && (factor < MPT_ULTRA2)) {
2750 factor = MPT_ULTRA2;
2751 }
2752
2753 /* Save the data to the target structure.
2754 */
2755 target->minSyncFactor = factor;
2756 target->maxOffset = offset;
2757 target->maxWidth = width;
2758
2759 target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
2760
2761 /* Disable unused features.
2762 */
2763 if (!width)
2764 target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
2765
2766 if (!offset)
2767 target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
2768
2769 if ( factor > MPT_ULTRA320 )
2770 noQas = 0;
2771
James Bottomleyc92f2222006-03-01 09:02:49 -06002772 if (noQas && (pspi_data->noQas == 0)) {
2773 pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS;
2774 target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775
James Bottomleyc92f2222006-03-01 09:02:49 -06002776 /* Disable QAS in a mixed configuration case
2777 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778
James Bottomleyc92f2222006-03-01 09:02:49 -06002779 ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 }
2781}
2782
2783/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784
2785/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2786/*
2787 * SCSI Config Page functionality ...
2788 */
2789/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2790/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags
2791 * based on width, factor and offset parameters.
2792 * @width: bus width
2793 * @factor: sync factor
2794 * @offset: sync offset
2795 * @requestedPtr: pointer to requested values (updated)
2796 * @configurationPtr: pointer to configuration values (updated)
2797 * @flags: flags to block WDTR or SDTR negotiation
2798 *
2799 * Return: None.
2800 *
2801 * Remark: Called by writeSDP1 and _dv_params
2802 */
2803static void
2804mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags)
2805{
2806 u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE;
2807 u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC;
2808
2809 *configurationPtr = 0;
2810 *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0;
2811 *requestedPtr |= (offset << 16) | (factor << 8);
2812
2813 if (width && offset && !nowide && !nosync) {
2814 if (factor < MPT_ULTRA160) {
2815 *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT);
2816 if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0)
2817 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS;
2818 if (flags & MPT_TAPE_NEGO_IDP)
2819 *requestedPtr |= 0x08000000;
2820 } else if (factor < MPT_ULTRA2) {
2821 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT;
2822 }
2823 }
2824
2825 if (nowide)
2826 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED;
2827
2828 if (nosync)
2829 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED;
2830
2831 return;
2832}
2833
2834/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835/* mptscsih_writeIOCPage4 - write IOC Page 4
2836 * @hd: Pointer to a SCSI Host Structure
2837 * @target_id: write IOC Page4 for this ID & Bus
2838 *
2839 * Return: -EAGAIN if unable to obtain a Message Frame
2840 * or 0 if success.
2841 *
2842 * Remark: We do not wait for a return, write pages sequentially.
2843 */
2844static int
2845mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus)
2846{
2847 MPT_ADAPTER *ioc = hd->ioc;
2848 Config_t *pReq;
2849 IOCPage4_t *IOCPage4Ptr;
2850 MPT_FRAME_HDR *mf;
2851 dma_addr_t dataDma;
2852 u16 req_idx;
2853 u32 frameOffset;
2854 u32 flagsLength;
2855 int ii;
2856
2857 /* Get a MF for this command.
2858 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002859 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002860 dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 ioc->name));
2862 return -EAGAIN;
2863 }
2864
2865 /* Set the request and the data pointers.
2866 * Place data at end of MF.
2867 */
2868 pReq = (Config_t *)mf;
2869
2870 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2871 frameOffset = ioc->req_sz - sizeof(IOCPage4_t);
2872
2873 /* Complete the request frame (same for all requests).
2874 */
2875 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
2876 pReq->Reserved = 0;
2877 pReq->ChainOffset = 0;
2878 pReq->Function = MPI_FUNCTION_CONFIG;
2879 pReq->ExtPageLength = 0;
2880 pReq->ExtPageType = 0;
2881 pReq->MsgFlags = 0;
2882 for (ii=0; ii < 8; ii++) {
2883 pReq->Reserved2[ii] = 0;
2884 }
2885
2886 IOCPage4Ptr = ioc->spi_data.pIocPg4;
2887 dataDma = ioc->spi_data.IocPg4_dma;
2888 ii = IOCPage4Ptr->ActiveSEP++;
2889 IOCPage4Ptr->SEP[ii].SEPTargetID = target_id;
2890 IOCPage4Ptr->SEP[ii].SEPBus = bus;
2891 pReq->Header = IOCPage4Ptr->Header;
2892 pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 ));
2893
2894 /* Add a SGE to the config request.
2895 */
2896 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
2897 (IOCPage4Ptr->Header.PageLength + ii) * 4;
2898
2899 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
2900
2901 dinitprintk((MYIOC_s_INFO_FMT
2902 "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
2903 ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus));
2904
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002905 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906
2907 return 0;
2908}
2909
2910/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2911/*
2912 * Bus Scan and Domain Validation functionality ...
2913 */
2914
2915/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2916/*
2917 * mptscsih_scandv_complete - Scan and DV callback routine registered
2918 * to Fustion MPT (base) driver.
2919 *
2920 * @ioc: Pointer to MPT_ADAPTER structure
2921 * @mf: Pointer to original MPT request frame
2922 * @mr: Pointer to MPT reply frame (NULL if TurboReply)
2923 *
2924 * This routine is called from mpt.c::mpt_interrupt() at the completion
2925 * of any SCSI IO request.
2926 * This routine is registered with the Fusion MPT (base) driver at driver
2927 * load/init time via the mpt_register() API call.
2928 *
2929 * Returns 1 indicating alloc'd request frame ptr should be freed.
2930 *
2931 * Remark: Sets a completion code and (possibly) saves sense data
2932 * in the IOC member localReply structure.
2933 * Used ONLY for DV and other internal commands.
2934 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002935int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
2937{
2938 MPT_SCSI_HOST *hd;
2939 SCSIIORequest_t *pReq;
2940 int completionCode;
2941 u16 req_idx;
2942
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002943 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2944
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 if ((mf == NULL) ||
2946 (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
2947 printk(MYIOC_s_ERR_FMT
2948 "ScanDvComplete, %s req frame ptr! (=%p)\n",
2949 ioc->name, mf?"BAD":"NULL", (void *) mf);
2950 goto wakeup;
2951 }
2952
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 del_timer(&hd->timer);
2954 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2955 hd->ScsiLookup[req_idx] = NULL;
2956 pReq = (SCSIIORequest_t *) mf;
2957
2958 if (mf != hd->cmdPtr) {
2959 printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
2960 hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
2961 }
2962 hd->cmdPtr = NULL;
2963
2964 ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
2965 hd->ioc->name, mf, mr, req_idx));
2966
2967 hd->pLocal = &hd->localReply;
2968 hd->pLocal->scsiStatus = 0;
2969
2970 /* If target struct exists, clear sense valid flag.
2971 */
2972 if (mr == NULL) {
2973 completionCode = MPT_SCANDV_GOOD;
2974 } else {
2975 SCSIIOReply_t *pReply;
2976 u16 status;
2977 u8 scsi_status;
2978
2979 pReply = (SCSIIOReply_t *) mr;
2980
2981 status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2982 scsi_status = pReply->SCSIStatus;
2983
2984 ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
2985 status, pReply->SCSIState, scsi_status,
2986 le32_to_cpu(pReply->IOCLogInfo)));
2987
2988 switch(status) {
2989
2990 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
2991 completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
2992 break;
2993
2994 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
2995 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
2996 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
2997 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
2998 completionCode = MPT_SCANDV_DID_RESET;
2999 break;
3000
3001 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
3002 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
3003 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
3004 if (pReply->Function == MPI_FUNCTION_CONFIG) {
3005 ConfigReply_t *pr = (ConfigReply_t *)mr;
3006 completionCode = MPT_SCANDV_GOOD;
3007 hd->pLocal->header.PageVersion = pr->Header.PageVersion;
3008 hd->pLocal->header.PageLength = pr->Header.PageLength;
3009 hd->pLocal->header.PageNumber = pr->Header.PageNumber;
3010 hd->pLocal->header.PageType = pr->Header.PageType;
3011
3012 } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
3013 /* If the RAID Volume request is successful,
3014 * return GOOD, else indicate that
3015 * some type of error occurred.
3016 */
3017 MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
Christoph Hellwig637fa992005-08-18 16:25:44 +02003018 if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019 completionCode = MPT_SCANDV_GOOD;
3020 else
3021 completionCode = MPT_SCANDV_SOME_ERROR;
James Bottomleyc92f2222006-03-01 09:02:49 -06003022 memcpy(hd->pLocal->sense, pr, sizeof(hd->pLocal->sense));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023
3024 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
3025 u8 *sense_data;
3026 int sz;
3027
3028 /* save sense data in global structure
3029 */
3030 completionCode = MPT_SCANDV_SENSE;
3031 hd->pLocal->scsiStatus = scsi_status;
3032 sense_data = ((u8 *)hd->ioc->sense_buf_pool +
3033 (req_idx * MPT_SENSE_BUFFER_ALLOC));
3034
3035 sz = min_t(int, pReq->SenseBufferLength,
3036 SCSI_STD_SENSE_BYTES);
3037 memcpy(hd->pLocal->sense, sense_data, sz);
3038
3039 ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
3040 sense_data));
3041 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
3042 if (pReq->CDB[0] == INQUIRY)
3043 completionCode = MPT_SCANDV_ISSUE_SENSE;
3044 else
3045 completionCode = MPT_SCANDV_DID_RESET;
3046 }
3047 else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
3048 completionCode = MPT_SCANDV_DID_RESET;
3049 else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3050 completionCode = MPT_SCANDV_DID_RESET;
3051 else {
3052 completionCode = MPT_SCANDV_GOOD;
3053 hd->pLocal->scsiStatus = scsi_status;
3054 }
3055 break;
3056
3057 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
3058 if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3059 completionCode = MPT_SCANDV_DID_RESET;
3060 else
3061 completionCode = MPT_SCANDV_SOME_ERROR;
3062 break;
3063
3064 default:
3065 completionCode = MPT_SCANDV_SOME_ERROR;
3066 break;
3067
3068 } /* switch(status) */
3069
3070 ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n",
3071 completionCode));
3072 } /* end of address reply case */
3073
3074 hd->pLocal->completion = completionCode;
3075
3076 /* MF and RF are freed in mpt_interrupt
3077 */
3078wakeup:
3079 /* Free Chain buffers (will never chain) in scan or dv */
3080 //mptscsih_freeChainBuffers(ioc, req_idx);
3081
3082 /*
3083 * Wake up the original calling thread
3084 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003085 hd->scandv_wait_done = 1;
3086 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087
3088 return 1;
3089}
3090
3091/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3092/* mptscsih_timer_expired - Call back for timer process.
3093 * Used only for dv functionality.
3094 * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
3095 *
3096 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003097void
3098mptscsih_timer_expired(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099{
3100 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
3101
3102 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
3103
3104 if (hd->cmdPtr) {
3105 MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
3106
3107 if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
3108 /* Desire to issue a task management request here.
3109 * TM requests MUST be single threaded.
3110 * If old eh code and no TM current, issue request.
3111 * If new eh code, do nothing. Wait for OS cmd timeout
3112 * for bus reset.
3113 */
3114 ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
3115 } else {
3116 /* Perform a FW reload */
3117 if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
3118 printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
3119 }
3120 }
3121 } else {
3122 /* This should NEVER happen */
3123 printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
3124 }
3125
3126 /* No more processing.
3127 * TM call will generate an interrupt for SCSI TM Management.
3128 * The FW will reply to all outstanding commands, callback will finish cleanup.
3129 * Hard reset clean-up will free all resources.
3130 */
3131 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
3132
3133 return;
3134}
3135
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136
3137/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3138/**
3139 * mptscsih_do_cmd - Do internal command.
3140 * @hd: MPT_SCSI_HOST pointer
3141 * @io: INTERNAL_CMD pointer.
3142 *
3143 * Issue the specified internally generated command and do command
3144 * specific cleanup. For bus scan / DV only.
3145 * NOTES: If command is Inquiry and status is good,
3146 * initialize a target structure, save the data
3147 *
3148 * Remark: Single threaded access only.
3149 *
3150 * Return:
3151 * < 0 if an illegal command or no resources
3152 *
3153 * 0 if good
3154 *
3155 * > 0 if command complete but some type of completion error.
3156 */
3157static int
3158mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
3159{
3160 MPT_FRAME_HDR *mf;
3161 SCSIIORequest_t *pScsiReq;
3162 SCSIIORequest_t ReqCopy;
3163 int my_idx, ii, dir;
3164 int rc, cmdTimeout;
3165 int in_isr;
3166 char cmdLen;
3167 char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3168 char cmd = io->cmd;
3169
3170 in_isr = in_interrupt();
3171 if (in_isr) {
3172 dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
3173 hd->ioc->name));
3174 return -EPERM;
3175 }
3176
3177
3178 /* Set command specific information
3179 */
3180 switch (cmd) {
3181 case INQUIRY:
3182 cmdLen = 6;
3183 dir = MPI_SCSIIO_CONTROL_READ;
3184 CDB[0] = cmd;
3185 CDB[4] = io->size;
3186 cmdTimeout = 10;
3187 break;
3188
3189 case TEST_UNIT_READY:
3190 cmdLen = 6;
3191 dir = MPI_SCSIIO_CONTROL_READ;
3192 cmdTimeout = 10;
3193 break;
3194
3195 case START_STOP:
3196 cmdLen = 6;
3197 dir = MPI_SCSIIO_CONTROL_READ;
3198 CDB[0] = cmd;
3199 CDB[4] = 1; /*Spin up the disk */
3200 cmdTimeout = 15;
3201 break;
3202
3203 case REQUEST_SENSE:
3204 cmdLen = 6;
3205 CDB[0] = cmd;
3206 CDB[4] = io->size;
3207 dir = MPI_SCSIIO_CONTROL_READ;
3208 cmdTimeout = 10;
3209 break;
3210
3211 case READ_BUFFER:
3212 cmdLen = 10;
3213 dir = MPI_SCSIIO_CONTROL_READ;
3214 CDB[0] = cmd;
3215 if (io->flags & MPT_ICFLAG_ECHO) {
3216 CDB[1] = 0x0A;
3217 } else {
3218 CDB[1] = 0x02;
3219 }
3220
3221 if (io->flags & MPT_ICFLAG_BUF_CAP) {
3222 CDB[1] |= 0x01;
3223 }
3224 CDB[6] = (io->size >> 16) & 0xFF;
3225 CDB[7] = (io->size >> 8) & 0xFF;
3226 CDB[8] = io->size & 0xFF;
3227 cmdTimeout = 10;
3228 break;
3229
3230 case WRITE_BUFFER:
3231 cmdLen = 10;
3232 dir = MPI_SCSIIO_CONTROL_WRITE;
3233 CDB[0] = cmd;
3234 if (io->flags & MPT_ICFLAG_ECHO) {
3235 CDB[1] = 0x0A;
3236 } else {
3237 CDB[1] = 0x02;
3238 }
3239 CDB[6] = (io->size >> 16) & 0xFF;
3240 CDB[7] = (io->size >> 8) & 0xFF;
3241 CDB[8] = io->size & 0xFF;
3242 cmdTimeout = 10;
3243 break;
3244
3245 case RESERVE:
3246 cmdLen = 6;
3247 dir = MPI_SCSIIO_CONTROL_READ;
3248 CDB[0] = cmd;
3249 cmdTimeout = 10;
3250 break;
3251
3252 case RELEASE:
3253 cmdLen = 6;
3254 dir = MPI_SCSIIO_CONTROL_READ;
3255 CDB[0] = cmd;
3256 cmdTimeout = 10;
3257 break;
3258
3259 case SYNCHRONIZE_CACHE:
3260 cmdLen = 10;
3261 dir = MPI_SCSIIO_CONTROL_READ;
3262 CDB[0] = cmd;
3263// CDB[1] = 0x02; /* set immediate bit */
3264 cmdTimeout = 10;
3265 break;
3266
3267 default:
3268 /* Error Case */
3269 return -EFAULT;
3270 }
3271
3272 /* Get and Populate a free Frame
3273 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003274 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003275 ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
3276 hd->ioc->name));
3277 return -EBUSY;
3278 }
3279
3280 pScsiReq = (SCSIIORequest_t *) mf;
3281
3282 /* Get the request index */
3283 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3284 ADD_INDEX_LOG(my_idx); /* for debug */
3285
3286 if (io->flags & MPT_ICFLAG_PHYS_DISK) {
3287 pScsiReq->TargetID = io->physDiskNum;
3288 pScsiReq->Bus = 0;
3289 pScsiReq->ChainOffset = 0;
3290 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
3291 } else {
3292 pScsiReq->TargetID = io->id;
3293 pScsiReq->Bus = io->bus;
3294 pScsiReq->ChainOffset = 0;
3295 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
3296 }
3297
3298 pScsiReq->CDBLength = cmdLen;
3299 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
3300
3301 pScsiReq->Reserved = 0;
3302
3303 pScsiReq->MsgFlags = mpt_msg_flags();
3304 /* MsgContext set in mpt_get_msg_fram call */
3305
3306 for (ii=0; ii < 8; ii++)
3307 pScsiReq->LUN[ii] = 0;
3308 pScsiReq->LUN[1] = io->lun;
3309
3310 if (io->flags & MPT_ICFLAG_TAGGED_CMD)
3311 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
3312 else
3313 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3314
3315 if (cmd == REQUEST_SENSE) {
3316 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3317 ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n",
3318 hd->ioc->name, cmd));
3319 }
3320
3321 for (ii=0; ii < 16; ii++)
3322 pScsiReq->CDB[ii] = CDB[ii];
3323
3324 pScsiReq->DataLength = cpu_to_le32(io->size);
3325 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
3326 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
3327
3328 ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
3329 hd->ioc->name, cmd, io->bus, io->id, io->lun));
3330
3331 if (dir == MPI_SCSIIO_CONTROL_READ) {
3332 mpt_add_sge((char *) &pScsiReq->SGL,
3333 MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
3334 io->data_dma);
3335 } else {
3336 mpt_add_sge((char *) &pScsiReq->SGL,
3337 MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
3338 io->data_dma);
3339 }
3340
3341 /* The ISR will free the request frame, but we need
3342 * the information to initialize the target. Duplicate.
3343 */
3344 memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
3345
3346 /* Issue this command after:
3347 * finish init
3348 * add timer
3349 * Wait until the reply has been received
3350 * ScsiScanDvCtx callback function will
3351 * set hd->pLocal;
3352 * set scandv_wait_done and call wake_up
3353 */
3354 hd->pLocal = NULL;
3355 hd->timer.expires = jiffies + HZ*cmdTimeout;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003356 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003357
3358 /* Save cmd pointer, for resource free if timeout or
3359 * FW reload occurs
3360 */
3361 hd->cmdPtr = mf;
3362
3363 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003364 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3365 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003366
3367 if (hd->pLocal) {
3368 rc = hd->pLocal->completion;
3369 hd->pLocal->skip = 0;
3370
3371 /* Always set fatal error codes in some cases.
3372 */
3373 if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
3374 rc = -ENXIO;
3375 else if (rc == MPT_SCANDV_SOME_ERROR)
3376 rc = -rc;
3377 } else {
3378 rc = -EFAULT;
3379 /* This should never happen. */
3380 ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
3381 hd->ioc->name));
3382 }
3383
3384 return rc;
3385}
3386
3387/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3388/**
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003389 * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
3390 * @hd: Pointer to a SCSI HOST structure
3391 * @vtarget: per device private data
3392 * @lun: lun
3393 *
3394 * Uses the ISR, but with special processing.
3395 * MUST be single-threaded.
3396 *
3397 */
3398static void
3399mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
3400{
3401 INTERNAL_CMD iocmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402
3403 /* Following parameters will not change
3404 * in this routine.
3405 */
3406 iocmd.cmd = SYNCHRONIZE_CACHE;
3407 iocmd.flags = 0;
3408 iocmd.physDiskNum = -1;
3409 iocmd.data = NULL;
3410 iocmd.data_dma = -1;
3411 iocmd.size = 0;
3412 iocmd.rsvd = iocmd.rsvd2 = 0;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003413 iocmd.bus = vdevice->bus_id;
3414 iocmd.id = vdevice->target_id;
3415 iocmd.lun = (u8)vdevice->lun;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003416
James Bottomleyc92f2222006-03-01 09:02:49 -06003417 if ((vdevice->vtarget->type == TYPE_DISK) &&
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003418 (vdevice->configured_lun))
3419 mptscsih_do_cmd(hd, &iocmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003420}
3421
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003422EXPORT_SYMBOL(mptscsih_remove);
3423EXPORT_SYMBOL(mptscsih_shutdown);
3424#ifdef CONFIG_PM
3425EXPORT_SYMBOL(mptscsih_suspend);
3426EXPORT_SYMBOL(mptscsih_resume);
3427#endif
3428EXPORT_SYMBOL(mptscsih_proc_info);
3429EXPORT_SYMBOL(mptscsih_info);
3430EXPORT_SYMBOL(mptscsih_qcmd);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003431EXPORT_SYMBOL(mptscsih_target_alloc);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003432EXPORT_SYMBOL(mptscsih_slave_alloc);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003433EXPORT_SYMBOL(mptscsih_target_destroy);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003434EXPORT_SYMBOL(mptscsih_slave_destroy);
3435EXPORT_SYMBOL(mptscsih_slave_configure);
3436EXPORT_SYMBOL(mptscsih_abort);
3437EXPORT_SYMBOL(mptscsih_dev_reset);
3438EXPORT_SYMBOL(mptscsih_bus_reset);
3439EXPORT_SYMBOL(mptscsih_host_reset);
3440EXPORT_SYMBOL(mptscsih_bios_param);
3441EXPORT_SYMBOL(mptscsih_io_done);
3442EXPORT_SYMBOL(mptscsih_taskmgmt_complete);
3443EXPORT_SYMBOL(mptscsih_scandv_complete);
3444EXPORT_SYMBOL(mptscsih_event_process);
3445EXPORT_SYMBOL(mptscsih_ioc_reset);
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06003446EXPORT_SYMBOL(mptscsih_change_queue_depth);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003447EXPORT_SYMBOL(mptscsih_timer_expired);
James Bottomley663e1aa2006-01-29 12:10:24 -06003448EXPORT_SYMBOL(mptscsih_TMHandler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003450/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/