blob: 58b5fdee009a767e51106e01a46a813ffae4a9f6 [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>
65
66#include "mptbase.h"
67#include "mptscsih.h"
68
69/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
70#define my_NAME "Fusion MPT SCSI Host driver"
71#define my_VERSION MPT_LINUX_VERSION_COMMON
72#define MYNAM "mptscsih"
73
74MODULE_AUTHOR(MODULEAUTHOR);
75MODULE_DESCRIPTION(my_NAME);
76MODULE_LICENSE("GPL");
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
79
80typedef struct _BIG_SENSE_BUF {
81 u8 data[MPT_SENSE_BUFFER_ALLOC];
82} BIG_SENSE_BUF;
83
84#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */
85#define MPT_SCANDV_DID_RESET (0x00000001)
86#define MPT_SCANDV_SENSE (0x00000002)
87#define MPT_SCANDV_SOME_ERROR (0x00000004)
88#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
89#define MPT_SCANDV_ISSUE_SENSE (0x00000010)
90#define MPT_SCANDV_FALLBACK (0x00000020)
91
92#define MPT_SCANDV_MAX_RETRIES (10)
93
94#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */
95#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */
96#define MPT_ICFLAG_PHYS_DISK 0x04 /* Any SCSI IO but do Phys Disk Format */
97#define MPT_ICFLAG_TAGGED_CMD 0x08 /* Do tagged IO */
98#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occurred with this command */
99#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */
100
101typedef struct _internal_cmd {
102 char *data; /* data pointer */
103 dma_addr_t data_dma; /* data dma address */
104 int size; /* transfer size */
105 u8 cmd; /* SCSI Op Code */
106 u8 bus; /* bus number */
107 u8 id; /* SCSI ID (virtual) */
108 u8 lun;
109 u8 flags; /* Bit Field - See above */
110 u8 physDiskNum; /* Phys disk number, -1 else */
111 u8 rsvd2;
112 u8 rsvd;
113} INTERNAL_CMD;
114
115typedef struct _negoparms {
116 u8 width;
117 u8 offset;
118 u8 factor;
119 u8 flags;
120} NEGOPARMS;
121
122typedef struct _dv_parameters {
123 NEGOPARMS max;
124 NEGOPARMS now;
125 u8 cmd;
126 u8 id;
127 u16 pad1;
128} DVPARAMETERS;
129
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130/*
131 * Other private/forward protos...
132 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400133int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134static void mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400135int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
138 SCSIIORequest_t *pReq, int req_idx);
139static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400140static 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 -0700141static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
142static int mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
143static u32 SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc);
144
145static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);
146static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);
147
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400148int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
149int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
151static void mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen);
152static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56);
153static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);
154static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);
155static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id);
156static int mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags);
157static int mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400158int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
160static int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum);
161
162#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
163static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io);
164static void mptscsih_domainValidation(void *hd);
165static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);
166static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id);
167static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target);
168static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);
169static void mptscsih_fillbuf(char *buffer, int size, int index, int width);
170#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400172void mptscsih_remove(struct pci_dev *);
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -0700173void mptscsih_shutdown(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174#ifdef CONFIG_PM
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400175int mptscsih_suspend(struct pci_dev *pdev, pm_message_t state);
176int mptscsih_resume(struct pci_dev *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177#endif
178
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179#define SNS_LEN(scp) sizeof((scp)->sense_buffer)
180
181#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
182/*
183 * Domain Validation task structure
184 */
185static DEFINE_SPINLOCK(dvtaskQ_lock);
186static int dvtaskQ_active = 0;
187static int dvtaskQ_release = 0;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400188static struct work_struct dvTaskQ_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189#endif
190
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
192/**
193 * mptscsih_add_sge - Place a simple SGE at address pAddr.
194 * @pAddr: virtual address for SGE
195 * @flagslength: SGE flags and data transfer length
196 * @dma_addr: Physical address
197 *
198 * This routine places a MPT request frame back on the MPT adapter's
199 * FreeQ.
200 */
201static inline void
202mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
203{
204 if (sizeof(dma_addr_t) == sizeof(u64)) {
205 SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
206 u32 tmp = dma_addr & 0xFFFFFFFF;
207
208 pSge->FlagsLength = cpu_to_le32(flagslength);
209 pSge->Address.Low = cpu_to_le32(tmp);
210 tmp = (u32) ((u64)dma_addr >> 32);
211 pSge->Address.High = cpu_to_le32(tmp);
212
213 } else {
214 SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
215 pSge->FlagsLength = cpu_to_le32(flagslength);
216 pSge->Address = cpu_to_le32(dma_addr);
217 }
218} /* mptscsih_add_sge() */
219
220/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
221/**
222 * mptscsih_add_chain - Place a chain SGE at address pAddr.
223 * @pAddr: virtual address for SGE
224 * @next: nextChainOffset value (u32's)
225 * @length: length of next SGL segment
226 * @dma_addr: Physical address
227 *
228 * This routine places a MPT request frame back on the MPT adapter's
229 * FreeQ.
230 */
231static inline void
232mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
233{
234 if (sizeof(dma_addr_t) == sizeof(u64)) {
235 SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
236 u32 tmp = dma_addr & 0xFFFFFFFF;
237
238 pChain->Length = cpu_to_le16(length);
239 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
240
241 pChain->NextChainOffset = next;
242
243 pChain->Address.Low = cpu_to_le32(tmp);
244 tmp = (u32) ((u64)dma_addr >> 32);
245 pChain->Address.High = cpu_to_le32(tmp);
246 } else {
247 SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
248 pChain->Length = cpu_to_le16(length);
249 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
250 pChain->NextChainOffset = next;
251 pChain->Address = cpu_to_le32(dma_addr);
252 }
253} /* mptscsih_add_chain() */
254
255/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
256/*
257 * mptscsih_getFreeChainBuffer - Function to get a free chain
258 * from the MPT_SCSI_HOST FreeChainQ.
259 * @ioc: Pointer to MPT_ADAPTER structure
260 * @req_idx: Index of the SCSI IO request frame. (output)
261 *
262 * return SUCCESS or FAILED
263 */
264static inline int
265mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex)
266{
267 MPT_FRAME_HDR *chainBuf;
268 unsigned long flags;
269 int rc;
270 int chain_idx;
271
272 dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer called\n",
273 ioc->name));
274 spin_lock_irqsave(&ioc->FreeQlock, flags);
275 if (!list_empty(&ioc->FreeChainQ)) {
276 int offset;
277
278 chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR,
279 u.frame.linkage.list);
280 list_del(&chainBuf->u.frame.linkage.list);
281 offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer;
282 chain_idx = offset / ioc->req_sz;
283 rc = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200284 dsgprintk((MYIOC_s_ERR_FMT "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n",
285 ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 } else {
287 rc = FAILED;
288 chain_idx = MPT_HOST_NO_CHAIN;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200289 dfailprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer failed\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 ioc->name));
291 }
292 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
293
294 *retIndex = chain_idx;
295 return rc;
296} /* mptscsih_getFreeChainBuffer() */
297
298/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
299/*
300 * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
301 * SCSIIORequest_t Message Frame.
302 * @ioc: Pointer to MPT_ADAPTER structure
303 * @SCpnt: Pointer to scsi_cmnd structure
304 * @pReq: Pointer to SCSIIORequest_t structure
305 *
306 * Returns ...
307 */
308static int
309mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
310 SCSIIORequest_t *pReq, int req_idx)
311{
312 char *psge;
313 char *chainSge;
314 struct scatterlist *sg;
315 int frm_sz;
316 int sges_left, sg_done;
317 int chain_idx = MPT_HOST_NO_CHAIN;
318 int sgeOffset;
319 int numSgeSlots, numSgeThisFrame;
320 u32 sgflags, sgdir, thisxfer = 0;
321 int chain_dma_off = 0;
322 int newIndex;
323 int ii;
324 dma_addr_t v2;
325 u32 RequestNB;
326
327 sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
328 if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
329 sgdir = MPT_TRANSFER_HOST_TO_IOC;
330 } else {
331 sgdir = MPT_TRANSFER_IOC_TO_HOST;
332 }
333
334 psge = (char *) &pReq->SGL;
335 frm_sz = ioc->req_sz;
336
337 /* Map the data portion, if any.
338 * sges_left = 0 if no data transfer.
339 */
340 if ( (sges_left = SCpnt->use_sg) ) {
341 sges_left = pci_map_sg(ioc->pcidev,
342 (struct scatterlist *) SCpnt->request_buffer,
343 SCpnt->use_sg,
344 SCpnt->sc_data_direction);
345 if (sges_left == 0)
346 return FAILED;
347 } else if (SCpnt->request_bufflen) {
348 SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev,
349 SCpnt->request_buffer,
350 SCpnt->request_bufflen,
351 SCpnt->sc_data_direction);
352 dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
353 ioc->name, SCpnt, SCpnt->request_bufflen));
354 mptscsih_add_sge((char *) &pReq->SGL,
355 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
356 SCpnt->SCp.dma_handle);
357
358 return SUCCESS;
359 }
360
361 /* Handle the SG case.
362 */
363 sg = (struct scatterlist *) SCpnt->request_buffer;
364 sg_done = 0;
365 sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
366 chainSge = NULL;
367
368 /* Prior to entering this loop - the following must be set
369 * current MF: sgeOffset (bytes)
370 * chainSge (Null if original MF is not a chain buffer)
371 * sg_done (num SGE done for this MF)
372 */
373
374nextSGEset:
375 numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
376 numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
377
378 sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
379
380 /* Get first (num - 1) SG elements
381 * Skip any SG entries with a length of 0
382 * NOTE: at finish, sg and psge pointed to NEXT data/location positions
383 */
384 for (ii=0; ii < (numSgeThisFrame-1); ii++) {
385 thisxfer = sg_dma_len(sg);
386 if (thisxfer == 0) {
387 sg ++; /* Get next SG element from the OS */
388 sg_done++;
389 continue;
390 }
391
392 v2 = sg_dma_address(sg);
393 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
394
395 sg++; /* Get next SG element from the OS */
396 psge += (sizeof(u32) + sizeof(dma_addr_t));
397 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
398 sg_done++;
399 }
400
401 if (numSgeThisFrame == sges_left) {
402 /* Add last element, end of buffer and end of list flags.
403 */
404 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
405 MPT_SGE_FLAGS_END_OF_BUFFER |
406 MPT_SGE_FLAGS_END_OF_LIST;
407
408 /* Add last SGE and set termination flags.
409 * Note: Last SGE may have a length of 0 - which should be ok.
410 */
411 thisxfer = sg_dma_len(sg);
412
413 v2 = sg_dma_address(sg);
414 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
415 /*
416 sg++;
417 psge += (sizeof(u32) + sizeof(dma_addr_t));
418 */
419 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
420 sg_done++;
421
422 if (chainSge) {
423 /* The current buffer is a chain buffer,
424 * but there is not another one.
425 * Update the chain element
426 * Offset and Length fields.
427 */
428 mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
429 } else {
430 /* The current buffer is the original MF
431 * and there is no Chain buffer.
432 */
433 pReq->ChainOffset = 0;
434 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200435 dsgprintk((MYIOC_s_INFO_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
437 ioc->RequestNB[req_idx] = RequestNB;
438 }
439 } else {
440 /* At least one chain buffer is needed.
441 * Complete the first MF
442 * - last SGE element, set the LastElement bit
443 * - set ChainOffset (words) for orig MF
444 * (OR finish previous MF chain buffer)
445 * - update MFStructPtr ChainIndex
446 * - Populate chain element
447 * Also
448 * Loop until done.
449 */
450
451 dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
452 ioc->name, sg_done));
453
454 /* Set LAST_ELEMENT flag for last non-chain element
455 * in the buffer. Since psge points at the NEXT
456 * SGE element, go back one SGE element, update the flags
457 * and reset the pointer. (Note: sgflags & thisxfer are already
458 * set properly).
459 */
460 if (sg_done) {
461 u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
462 sgflags = le32_to_cpu(*ptmp);
463 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
464 *ptmp = cpu_to_le32(sgflags);
465 }
466
467 if (chainSge) {
468 /* The current buffer is a chain buffer.
469 * chainSge points to the previous Chain Element.
470 * Update its chain element Offset and Length (must
471 * include chain element size) fields.
472 * Old chain element is now complete.
473 */
474 u8 nextChain = (u8) (sgeOffset >> 2);
475 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
476 mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
477 } else {
478 /* The original MF buffer requires a chain buffer -
479 * set the offset.
480 * Last element in this MF is a chain element.
481 */
482 pReq->ChainOffset = (u8) (sgeOffset >> 2);
483 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
484 dsgprintk((MYIOC_s_ERR_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
485 ioc->RequestNB[req_idx] = RequestNB;
486 }
487
488 sges_left -= sg_done;
489
490
491 /* NOTE: psge points to the beginning of the chain element
492 * in current buffer. Get a chain buffer.
493 */
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200494 if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) {
495 dfailprintk((MYIOC_s_INFO_FMT
496 "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n",
497 ioc->name, pReq->CDB[0], SCpnt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 return FAILED;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200499 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
501 /* Update the tracking arrays.
502 * If chainSge == NULL, update ReqToChain, else ChainToChain
503 */
504 if (chainSge) {
505 ioc->ChainToChain[chain_idx] = newIndex;
506 } else {
507 ioc->ReqToChain[req_idx] = newIndex;
508 }
509 chain_idx = newIndex;
510 chain_dma_off = ioc->req_sz * chain_idx;
511
512 /* Populate the chainSGE for the current buffer.
513 * - Set chain buffer pointer to psge and fill
514 * out the Address and Flags fields.
515 */
516 chainSge = (char *) psge;
517 dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
518 psge, req_idx));
519
520 /* Start the SGE for the next buffer
521 */
522 psge = (char *) (ioc->ChainBuffer + chain_dma_off);
523 sgeOffset = 0;
524 sg_done = 0;
525
526 dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
527 psge, chain_idx));
528
529 /* Start the SGE for the next buffer
530 */
531
532 goto nextSGEset;
533 }
534
535 return SUCCESS;
536} /* mptscsih_AddSGE() */
537
538/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
539/*
540 * mptscsih_io_done - Main SCSI IO callback routine registered to
541 * Fusion MPT (base) driver
542 * @ioc: Pointer to MPT_ADAPTER structure
543 * @mf: Pointer to original MPT request frame
544 * @r: Pointer to MPT reply frame (NULL if TurboReply)
545 *
546 * This routine is called from mpt.c::mpt_interrupt() at the completion
547 * of any SCSI IO request.
548 * This routine is registered with the Fusion MPT (base) driver at driver
549 * load/init time via the mpt_register() API call.
550 *
551 * Returns 1 indicating alloc'd request frame ptr should be freed.
552 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400553int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
555{
556 struct scsi_cmnd *sc;
557 MPT_SCSI_HOST *hd;
558 SCSIIORequest_t *pScsiReq;
559 SCSIIOReply_t *pScsiReply;
560 u16 req_idx;
561
562 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
563
564 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
565 sc = hd->ScsiLookup[req_idx];
566 if (sc == NULL) {
567 MPIHeader_t *hdr = (MPIHeader_t *)mf;
568
569 /* Remark: writeSDP1 will use the ScsiDoneCtx
570 * If a SCSI I/O cmd, device disabled by OS and
571 * completion done. Cannot touch sc struct. Just free mem.
572 */
573 if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
574 printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n",
575 ioc->name);
576
577 mptscsih_freeChainBuffers(ioc, req_idx);
578 return 1;
579 }
580
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 sc->result = DID_OK << 16; /* Set default reply as OK */
582 pScsiReq = (SCSIIORequest_t *) mf;
583 pScsiReply = (SCSIIOReply_t *) mr;
584
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200585 if((ioc->facts.MsgVersion >= MPI_VERSION_01_05) && pScsiReply){
586 dmfprintk((MYIOC_s_INFO_FMT
587 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d,task-tag=%d)\n",
588 ioc->name, mf, mr, sc, req_idx, pScsiReply->TaskTag));
589 }else{
590 dmfprintk((MYIOC_s_INFO_FMT
591 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n",
592 ioc->name, mf, mr, sc, req_idx));
593 }
594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 if (pScsiReply == NULL) {
596 /* special context reply handling */
597 ;
598 } else {
599 u32 xfer_cnt;
600 u16 status;
601 u8 scsi_state, scsi_status;
602
603 status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
604 scsi_state = pScsiReply->SCSIState;
605 scsi_status = pScsiReply->SCSIStatus;
606 xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
607 sc->resid = sc->request_bufflen - xfer_cnt;
608
609 dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n"
610 "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n"
611 "resid=%d bufflen=%d xfer_cnt=%d\n",
612 ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1],
613 status, scsi_state, scsi_status, sc->resid,
614 sc->request_bufflen, xfer_cnt));
615
616 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400617 mptscsih_copy_sense_data(sc, hd, mf, pScsiReply);
618
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 /*
620 * Look for + dump FCP ResponseInfo[]!
621 */
622 if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
623 printk(KERN_NOTICE " FCP_ResponseInfo=%08xh\n",
624 le32_to_cpu(pScsiReply->ResponseInfo));
625 }
626
627 switch(status) {
628 case MPI_IOCSTATUS_BUSY: /* 0x0002 */
629 /* CHECKME!
630 * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
631 * But not: DID_BUS_BUSY lest one risk
632 * killing interrupt handler:-(
633 */
634 sc->result = SAM_STAT_BUSY;
635 break;
636
637 case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
638 case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
639 sc->result = DID_BAD_TARGET << 16;
640 break;
641
642 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
643 /* Spoof to SCSI Selection Timeout! */
644 sc->result = DID_NO_CONNECT << 16;
645
646 if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF)
647 hd->sel_timeout[pScsiReq->TargetID]++;
648 break;
649
650 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
651 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
652 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
653 /* Linux handles an unsolicited DID_RESET better
654 * than an unsolicited DID_ABORT.
655 */
656 sc->result = DID_RESET << 16;
657
658 /* GEM Workaround. */
659 if (ioc->bus_type == SCSI)
660 mptscsih_no_negotiate(hd, sc->device->id);
661 break;
662
663 case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
664 if ( xfer_cnt >= sc->underflow ) {
665 /* Sufficient data transfer occurred */
666 sc->result = (DID_OK << 16) | scsi_status;
667 } else if ( xfer_cnt == 0 ) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200668 /* A CRC Error causes this condition; retry */
669 sc->result = (DRIVER_SENSE << 24) | (DID_OK << 16) |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 (CHECK_CONDITION << 1);
671 sc->sense_buffer[0] = 0x70;
672 sc->sense_buffer[2] = NO_SENSE;
673 sc->sense_buffer[12] = 0;
674 sc->sense_buffer[13] = 0;
675 } else {
676 sc->result = DID_SOFT_ERROR << 16;
677 }
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200678 dreplyprintk((KERN_NOTICE
679 "RESIDUAL_MISMATCH: result=%x on id=%d\n",
680 sc->result, sc->device->id));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 break;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
684 /*
685 * Do upfront check for valid SenseData and give it
686 * precedence!
687 */
688 sc->result = (DID_OK << 16) | scsi_status;
689 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
690 /* Have already saved the status and sense data
691 */
692 ;
693 } else {
694 if (xfer_cnt < sc->underflow) {
695 sc->result = DID_SOFT_ERROR << 16;
696 }
697 if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
698 /* What to do?
699 */
700 sc->result = DID_SOFT_ERROR << 16;
701 }
702 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
703 /* Not real sure here either... */
704 sc->result = DID_RESET << 16;
705 }
706 }
707
708 dreplyprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n",
709 sc->underflow));
710 dreplyprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt));
711 /* Report Queue Full
712 */
713 if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)
714 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 break;
717
718 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
719 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
720 scsi_status = pScsiReply->SCSIStatus;
721 sc->result = (DID_OK << 16) | scsi_status;
722 if (scsi_state == 0) {
723 ;
724 } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
725 /*
726 * If running against circa 200003dd 909 MPT f/w,
727 * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
728 * (QUEUE_FULL) returned from device! --> get 0x0000?128
729 * and with SenseBytes set to 0.
730 */
731 if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
732 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
733
734 }
735 else if (scsi_state &
736 (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
737 ) {
738 /*
739 * What to do?
740 */
741 sc->result = DID_SOFT_ERROR << 16;
742 }
743 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
744 /* Not real sure here either... */
745 sc->result = DID_RESET << 16;
746 }
747 else if (scsi_state & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) {
748 /* Device Inq. data indicates that it supports
749 * QTags, but rejects QTag messages.
750 * This command completed OK.
751 *
752 * Not real sure here either so do nothing... */
753 }
754
755 if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
756 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
757
758 /* Add handling of:
759 * Reservation Conflict, Busy,
760 * Command Terminated, CHECK
761 */
762 break;
763
764 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
765 sc->result = DID_SOFT_ERROR << 16;
766 break;
767
768 case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
769 case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
770 case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
771 case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
772 case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
773 case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
774 case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
775 case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
776 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
777 case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
778 default:
779 /*
780 * What to do?
781 */
782 sc->result = DID_SOFT_ERROR << 16;
783 break;
784
785 } /* switch(status) */
786
787 dreplyprintk((KERN_NOTICE " sc->result is %08xh\n", sc->result));
788 } /* end of address reply case */
789
790 /* Unmap the DMA buffers, if any. */
791 if (sc->use_sg) {
792 pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
793 sc->use_sg, sc->sc_data_direction);
794 } else if (sc->request_bufflen) {
795 pci_unmap_single(ioc->pcidev, sc->SCp.dma_handle,
796 sc->request_bufflen, sc->sc_data_direction);
797 }
798
799 hd->ScsiLookup[req_idx] = NULL;
800
801 sc->scsi_done(sc); /* Issue the command callback */
802
803 /* Free Chain buffers */
804 mptscsih_freeChainBuffers(ioc, req_idx);
805 return 1;
806}
807
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808/*
809 * mptscsih_flush_running_cmds - For each command found, search
810 * Scsi_Host instance taskQ and reply to OS.
811 * Called only if recovering from a FW reload.
812 * @hd: Pointer to a SCSI HOST structure
813 *
814 * Returns: None.
815 *
816 * Must be called while new I/Os are being queued.
817 */
818static void
819mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
820{
821 MPT_ADAPTER *ioc = hd->ioc;
822 struct scsi_cmnd *SCpnt;
823 MPT_FRAME_HDR *mf;
824 int ii;
825 int max = ioc->req_depth;
826
827 dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n"));
828 for (ii= 0; ii < max; ii++) {
829 if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
830
831 /* Command found.
832 */
833
834 /* Null ScsiLookup index
835 */
836 hd->ScsiLookup[ii] = NULL;
837
838 mf = MPT_INDEX_2_MFPTR(ioc, ii);
839 dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n",
840 mf, SCpnt));
841
842 /* Set status, free OS resources (SG DMA buffers)
843 * Do OS callback
844 * Free driver resources (chain, msg buffers)
845 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400846 if (SCpnt->use_sg) {
847 pci_unmap_sg(ioc->pcidev,
848 (struct scatterlist *) SCpnt->request_buffer,
849 SCpnt->use_sg,
850 SCpnt->sc_data_direction);
851 } else if (SCpnt->request_bufflen) {
852 pci_unmap_single(ioc->pcidev,
853 SCpnt->SCp.dma_handle,
854 SCpnt->request_bufflen,
855 SCpnt->sc_data_direction);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 }
857 SCpnt->result = DID_RESET << 16;
858 SCpnt->host_scribble = NULL;
859
860 /* Free Chain buffers */
861 mptscsih_freeChainBuffers(ioc, ii);
862
863 /* Free Message frames */
864 mpt_free_msg_frame(ioc, mf);
865
866 SCpnt->scsi_done(SCpnt); /* Issue the command callback */
867 }
868 }
869
870 return;
871}
872
873/*
874 * mptscsih_search_running_cmds - Delete any commands associated
875 * with the specified target and lun. Function called only
876 * when a lun is disable by mid-layer.
877 * Do NOT access the referenced scsi_cmnd structure or
878 * members. Will cause either a paging or NULL ptr error.
879 * @hd: Pointer to a SCSI HOST structure
880 * @target: target id
881 * @lun: lun
882 *
883 * Returns: None.
884 *
885 * Called from slave_destroy.
886 */
887static void
888mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, uint target, uint lun)
889{
890 SCSIIORequest_t *mf = NULL;
891 int ii;
892 int max = hd->ioc->req_depth;
893
894 dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n",
895 target, lun, max));
896
897 for (ii=0; ii < max; ii++) {
898 if (hd->ScsiLookup[ii] != NULL) {
899
900 mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(hd->ioc, ii);
901
902 dsprintk(( "search_running: found (sc=%p, mf = %p) target %d, lun %d \n",
903 hd->ScsiLookup[ii], mf, mf->TargetID, mf->LUN[1]));
904
905 if ((mf->TargetID != ((u8)target)) || (mf->LUN[1] != ((u8) lun)))
906 continue;
907
908 /* Cleanup
909 */
910 hd->ScsiLookup[ii] = NULL;
911 mptscsih_freeChainBuffers(hd->ioc, ii);
912 mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf);
913 }
914 }
915
916 return;
917}
918
919/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
922/*
923 * mptscsih_report_queue_full - Report QUEUE_FULL status returned
924 * from a SCSI target device.
925 * @sc: Pointer to scsi_cmnd structure
926 * @pScsiReply: Pointer to SCSIIOReply_t
927 * @pScsiReq: Pointer to original SCSI request
928 *
929 * This routine periodically reports QUEUE_FULL status returned from a
930 * SCSI target device. It reports this to the console via kernel
931 * printk() API call, not more than once every 10 seconds.
932 */
933static void
934mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
935{
936 long time = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 MPT_SCSI_HOST *hd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400939 if (sc->device == NULL)
940 return;
941 if (sc->device->host == NULL)
942 return;
943 if ((hd = (MPT_SCSI_HOST *)sc->device->host->hostdata) == NULL)
944 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400946 if (time - hd->last_queue_full > 10 * HZ) {
947 dprintk((MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n",
948 hd->ioc->name, 0, sc->device->id, sc->device->lun));
949 hd->last_queue_full = time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951}
952
953/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
954/*
955 * mptscsih_remove - Removed scsi devices
956 * @pdev: Pointer to pci_dev structure
957 *
958 *
959 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400960void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961mptscsih_remove(struct pci_dev *pdev)
962{
963 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
964 struct Scsi_Host *host = ioc->sh;
965 MPT_SCSI_HOST *hd;
966 int count;
967 unsigned long flags;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400968 int sz1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969
970 if(!host)
971 return;
972
973 scsi_remove_host(host);
974
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400975 if((hd = (MPT_SCSI_HOST *)host->hostdata) == NULL)
976 return;
977
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
979 /* Check DV thread active */
980 count = 10 * HZ;
981 spin_lock_irqsave(&dvtaskQ_lock, flags);
982 if (dvtaskQ_active) {
983 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
984 while(dvtaskQ_active && --count) {
985 set_current_state(TASK_INTERRUPTIBLE);
986 schedule_timeout(1);
987 }
988 } else {
989 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
990 }
991 if (!count)
992 printk(KERN_ERR MYNAM ": ERROR - DV thread still active!\n");
993#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
994 else
995 printk(KERN_ERR MYNAM ": DV thread orig %d, count %d\n", 10 * HZ, count);
996#endif
997#endif
998
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -0700999 mptscsih_shutdown(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001001 sz1=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001003 if (hd->ScsiLookup != NULL) {
1004 sz1 = hd->ioc->req_depth * sizeof(void *);
1005 kfree(hd->ScsiLookup);
1006 hd->ScsiLookup = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 }
1008
Moore, Eric Dean d485eb82005-05-11 17:37:26 -06001009 /*
1010 * Free pointer array.
1011 */
1012 kfree(hd->Targets);
1013 hd->Targets = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001015 dprintk((MYIOC_s_INFO_FMT
1016 "Free'd ScsiLookup (%d) memory\n",
1017 hd->ioc->name, sz1));
1018
Moore, Eric Dean d485eb82005-05-11 17:37:26 -06001019 kfree(hd->info_kbuf);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001020
1021 /* NULL the Scsi_Host pointer
1022 */
1023 hd->ioc->sh = NULL;
1024
1025 scsi_host_put(host);
1026
1027 mpt_detach(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001028
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029}
1030
1031/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1032/*
1033 * mptscsih_shutdown - reboot notifier
1034 *
1035 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001036void
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001037mptscsih_shutdown(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001039 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 struct Scsi_Host *host = ioc->sh;
1041 MPT_SCSI_HOST *hd;
1042
1043 if(!host)
1044 return;
1045
1046 hd = (MPT_SCSI_HOST *)host->hostdata;
1047
1048 /* Flush the cache of this adapter
1049 */
1050 if(hd != NULL)
1051 mptscsih_synchronize_cache(hd, 0);
1052
1053}
1054
1055#ifdef CONFIG_PM
1056/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1057/*
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001058 * mptscsih_suspend - Fusion MPT scsi driver suspend routine.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 *
1060 *
1061 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001062int
Pavel Machek8d189f72005-04-16 15:25:28 -07001063mptscsih_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001065 mptscsih_shutdown(pdev);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001066 return mpt_suspend(pdev,state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067}
1068
1069/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1070/*
1071 * mptscsih_resume - Fusion MPT scsi driver resume routine.
1072 *
1073 *
1074 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001075int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076mptscsih_resume(struct pci_dev *pdev)
1077{
1078 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
1079 struct Scsi_Host *host = ioc->sh;
1080 MPT_SCSI_HOST *hd;
1081
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001082 mpt_resume(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001083
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 if(!host)
1085 return 0;
1086
1087 hd = (MPT_SCSI_HOST *)host->hostdata;
1088 if(!hd)
1089 return 0;
1090
1091#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1092 {
1093 unsigned long lflags;
1094 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1095 if (!dvtaskQ_active) {
1096 dvtaskQ_active = 1;
1097 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001098 INIT_WORK(&dvTaskQ_task,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 mptscsih_domainValidation, (void *) hd);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001100 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 } else {
1102 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1103 }
1104 }
1105#endif
1106 return 0;
1107}
1108
1109#endif
1110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1112/**
1113 * mptscsih_info - Return information about MPT adapter
1114 * @SChost: Pointer to Scsi_Host structure
1115 *
1116 * (linux scsi_host_template.info routine)
1117 *
1118 * Returns pointer to buffer where information was written.
1119 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001120const char *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121mptscsih_info(struct Scsi_Host *SChost)
1122{
1123 MPT_SCSI_HOST *h;
1124 int size = 0;
1125
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 h = (MPT_SCSI_HOST *)SChost->hostdata;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 if (h) {
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001129 if (h->info_kbuf == NULL)
1130 if ((h->info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
1131 return h->info_kbuf;
1132 h->info_kbuf[0] = '\0';
1133
1134 mpt_print_ioc_summary(h->ioc, h->info_kbuf, &size, 0, 0);
1135 h->info_kbuf[size-1] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 }
1137
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001138 return h->info_kbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139}
1140
1141struct info_str {
1142 char *buffer;
1143 int length;
1144 int offset;
1145 int pos;
1146};
1147
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001148static void
1149mptscsih_copy_mem_info(struct info_str *info, char *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150{
1151 if (info->pos + len > info->length)
1152 len = info->length - info->pos;
1153
1154 if (info->pos + len < info->offset) {
1155 info->pos += len;
1156 return;
1157 }
1158
1159 if (info->pos < info->offset) {
1160 data += (info->offset - info->pos);
1161 len -= (info->offset - info->pos);
1162 }
1163
1164 if (len > 0) {
1165 memcpy(info->buffer + info->pos, data, len);
1166 info->pos += len;
1167 }
1168}
1169
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001170static int
1171mptscsih_copy_info(struct info_str *info, char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172{
1173 va_list args;
1174 char buf[81];
1175 int len;
1176
1177 va_start(args, fmt);
1178 len = vsprintf(buf, fmt, args);
1179 va_end(args);
1180
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001181 mptscsih_copy_mem_info(info, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 return len;
1183}
1184
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001185static int
1186mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187{
1188 struct info_str info;
1189
1190 info.buffer = pbuf;
1191 info.length = len;
1192 info.offset = offset;
1193 info.pos = 0;
1194
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001195 mptscsih_copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name);
1196 mptscsih_copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word);
1197 mptscsih_copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts);
1198 mptscsih_copy_info(&info, "MaxQ=%d\n", ioc->req_depth);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
1200 return ((info.pos > info.offset) ? info.pos - info.offset : 0);
1201}
1202
1203/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1204/**
1205 * mptscsih_proc_info - Return information about MPT adapter
1206 *
1207 * (linux scsi_host_template.info routine)
1208 *
1209 * buffer: if write, user data; if read, buffer for user
1210 * length: if write, return length;
1211 * offset: if write, 0; if read, the current offset into the buffer from
1212 * the previous read.
1213 * hostno: scsi host number
1214 * func: if write = 1; if read = 0
1215 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001216int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
1218 int length, int func)
1219{
1220 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
1221 MPT_ADAPTER *ioc = hd->ioc;
1222 int size = 0;
1223
1224 if (func) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001225 /*
1226 * write is not supported
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 */
1228 } else {
1229 if (start)
1230 *start = buffer;
1231
1232 size = mptscsih_host_info(ioc, buffer, offset, length);
1233 }
1234
1235 return size;
1236}
1237
1238/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1239#define ADD_INDEX_LOG(req_ent) do { } while(0)
1240
1241/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1242/**
1243 * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
1244 * @SCpnt: Pointer to scsi_cmnd structure
1245 * @done: Pointer SCSI mid-layer IO completion function
1246 *
1247 * (linux scsi_host_template.queuecommand routine)
1248 * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
1249 * from a linux scsi_cmnd request and send it to the IOC.
1250 *
1251 * Returns 0. (rtn value discarded by linux scsi mid-layer)
1252 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001253int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
1255{
1256 MPT_SCSI_HOST *hd;
1257 MPT_FRAME_HDR *mf;
1258 SCSIIORequest_t *pScsiReq;
Christoph Hellwig82ffb6712005-09-09 16:25:54 +02001259 VirtDevice *pTarget = SCpnt->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 int lun;
1261 u32 datalen;
1262 u32 scsictl;
1263 u32 scsidir;
1264 u32 cmd_len;
1265 int my_idx;
1266 int ii;
1267
1268 hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 lun = SCpnt->device->lun;
1270 SCpnt->scsi_done = done;
1271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
1273 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
1274
1275 if (hd->resetPending) {
1276 dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
1277 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
1278 return SCSI_MLQUEUE_HOST_BUSY;
1279 }
1280
1281 /*
1282 * Put together a MPT SCSI request...
1283 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001284 if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
1286 hd->ioc->name));
1287 return SCSI_MLQUEUE_HOST_BUSY;
1288 }
1289
1290 pScsiReq = (SCSIIORequest_t *) mf;
1291
1292 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
1293
1294 ADD_INDEX_LOG(my_idx);
1295
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001296 /* TUR's being issued with scsictl=0x02000000 (DATA_IN)!
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 * Seems we may receive a buffer (datalen>0) even when there
1298 * will be no data transfer! GRRRRR...
1299 */
1300 if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
1301 datalen = SCpnt->request_bufflen;
1302 scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
1303 } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
1304 datalen = SCpnt->request_bufflen;
1305 scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
1306 } else {
1307 datalen = 0;
1308 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
1309 }
1310
1311 /* Default to untagged. Once a target structure has been allocated,
1312 * use the Inquiry data to determine if device supports tagged.
1313 */
Christoph Hellwig82ffb6712005-09-09 16:25:54 +02001314 if (pTarget
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
1316 && (SCpnt->device->tagged_supported)) {
1317 scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
1318 } else {
1319 scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
1320 }
1321
1322 /* Use the above information to set up the message frame
1323 */
Christoph Hellwig82ffb6712005-09-09 16:25:54 +02001324 pScsiReq->TargetID = (u8) pTarget->target_id;
1325 pScsiReq->Bus = pTarget->bus_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 pScsiReq->ChainOffset = 0;
1327 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
1328 pScsiReq->CDBLength = SCpnt->cmd_len;
1329 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
1330 pScsiReq->Reserved = 0;
1331 pScsiReq->MsgFlags = mpt_msg_flags();
1332 pScsiReq->LUN[0] = 0;
1333 pScsiReq->LUN[1] = lun;
1334 pScsiReq->LUN[2] = 0;
1335 pScsiReq->LUN[3] = 0;
1336 pScsiReq->LUN[4] = 0;
1337 pScsiReq->LUN[5] = 0;
1338 pScsiReq->LUN[6] = 0;
1339 pScsiReq->LUN[7] = 0;
1340 pScsiReq->Control = cpu_to_le32(scsictl);
1341
1342 /*
1343 * Write SCSI CDB into the message
1344 */
1345 cmd_len = SCpnt->cmd_len;
1346 for (ii=0; ii < cmd_len; ii++)
1347 pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
1348
1349 for (ii=cmd_len; ii < 16; ii++)
1350 pScsiReq->CDB[ii] = 0;
1351
1352 /* DataLength */
1353 pScsiReq->DataLength = cpu_to_le32(datalen);
1354
1355 /* SenseBuffer low address */
1356 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
1357 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
1358
1359 /* Now add the SG list
1360 * Always have a SGE even if null length.
1361 */
1362 if (datalen == 0) {
1363 /* Add a NULL SGE */
1364 mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
1365 (dma_addr_t) -1);
1366 } else {
1367 /* Add a 32 or 64 bit SGE */
1368 if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS)
1369 goto fail;
1370 }
1371
1372 hd->ScsiLookup[my_idx] = SCpnt;
1373 SCpnt->host_scribble = NULL;
1374
1375#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1376 if (hd->ioc->bus_type == SCSI) {
Christoph Hellwig82ffb6712005-09-09 16:25:54 +02001377 int dvStatus = hd->ioc->spi_data.dvStatus[pTarget->target_id];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 int issueCmd = 1;
1379
1380 if (dvStatus || hd->ioc->spi_data.forceDv) {
1381
1382 if ((dvStatus & MPT_SCSICFG_NEED_DV) ||
1383 (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) {
1384 unsigned long lflags;
1385 /* Schedule DV if necessary */
1386 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1387 if (!dvtaskQ_active) {
1388 dvtaskQ_active = 1;
1389 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001390 INIT_WORK(&dvTaskQ_task, mptscsih_domainValidation, (void *) hd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001392 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 } else {
1394 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1395 }
1396 hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV;
1397 }
1398
1399 /* Trying to do DV to this target, extend timeout.
1400 * Wait to issue until flag is clear
1401 */
1402 if (dvStatus & MPT_SCSICFG_DV_PENDING) {
1403 mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
1404 issueCmd = 0;
1405 }
1406
1407 /* Set the DV flags.
1408 */
1409 if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
1410 mptscsih_set_dvflags(hd, pScsiReq);
1411
1412 if (!issueCmd)
1413 goto fail;
1414 }
1415 }
1416#endif
1417
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001418 mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
1420 hd->ioc->name, SCpnt, mf, my_idx));
1421 DBG_DUMP_REQUEST_FRAME(mf)
1422 return 0;
1423
1424 fail:
1425 mptscsih_freeChainBuffers(hd->ioc, my_idx);
1426 mpt_free_msg_frame(hd->ioc, mf);
1427 return SCSI_MLQUEUE_HOST_BUSY;
1428}
1429
1430/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1431/*
1432 * mptscsih_freeChainBuffers - Function to free chain buffers associated
1433 * with a SCSI IO request
1434 * @hd: Pointer to the MPT_SCSI_HOST instance
1435 * @req_idx: Index of the SCSI IO request frame.
1436 *
1437 * Called if SG chain buffer allocation fails and mptscsih callbacks.
1438 * No return.
1439 */
1440static void
1441mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
1442{
1443 MPT_FRAME_HDR *chain;
1444 unsigned long flags;
1445 int chain_idx;
1446 int next;
1447
1448 /* Get the first chain index and reset
1449 * tracker state.
1450 */
1451 chain_idx = ioc->ReqToChain[req_idx];
1452 ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
1453
1454 while (chain_idx != MPT_HOST_NO_CHAIN) {
1455
1456 /* Save the next chain buffer index */
1457 next = ioc->ChainToChain[chain_idx];
1458
1459 /* Free this chain buffer and reset
1460 * tracker
1461 */
1462 ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
1463
1464 chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer
1465 + (chain_idx * ioc->req_sz));
1466
1467 spin_lock_irqsave(&ioc->FreeQlock, flags);
1468 list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ);
1469 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
1470
1471 dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
1472 ioc->name, chain_idx));
1473
1474 /* handle next */
1475 chain_idx = next;
1476 }
1477 return;
1478}
1479
1480/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1481/*
1482 * Reset Handling
1483 */
1484
1485/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1486/*
1487 * mptscsih_TMHandler - Generic handler for SCSI Task Management.
1488 * Fall through to mpt_HardResetHandler if: not operational, too many
1489 * failed TM requests or handshake failure.
1490 *
1491 * @ioc: Pointer to MPT_ADAPTER structure
1492 * @type: Task Management type
1493 * @target: Logical Target ID for reset (if appropriate)
1494 * @lun: Logical Unit for reset (if appropriate)
1495 * @ctx2abort: Context for the task to be aborted (if appropriate)
1496 *
1497 * Remark: Currently invoked from a non-interrupt thread (_bh).
1498 *
1499 * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
1500 * will be active.
1501 *
1502 * Returns 0 for SUCCESS or -1 if FAILED.
1503 */
1504static int
1505mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1506{
1507 MPT_ADAPTER *ioc;
1508 int rc = -1;
1509 int doTask = 1;
1510 u32 ioc_raw_state;
1511 unsigned long flags;
1512
1513 /* If FW is being reloaded currently, return success to
1514 * the calling function.
1515 */
1516 if (hd == NULL)
1517 return 0;
1518
1519 ioc = hd->ioc;
1520 if (ioc == NULL) {
1521 printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
1522 return FAILED;
1523 }
1524 dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
1525
1526 // SJR - CHECKME - Can we avoid this here?
1527 // (mpt_HardResetHandler has this check...)
1528 spin_lock_irqsave(&ioc->diagLock, flags);
1529 if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
1530 spin_unlock_irqrestore(&ioc->diagLock, flags);
1531 return FAILED;
1532 }
1533 spin_unlock_irqrestore(&ioc->diagLock, flags);
1534
1535 /* Wait a fixed amount of time for the TM pending flag to be cleared.
1536 * If we time out and not bus reset, then we return a FAILED status to the caller.
1537 * The call to mptscsih_tm_pending_wait() will set the pending flag if we are
1538 * successful. Otherwise, reload the FW.
1539 */
1540 if (mptscsih_tm_pending_wait(hd) == FAILED) {
1541 if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001542 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 "Timed out waiting for last TM (%d) to complete! \n",
1544 hd->ioc->name, hd->tmPending));
1545 return FAILED;
1546 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001547 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 "Timed out waiting for last TM (%d) to complete! \n",
1549 hd->ioc->name, hd->tmPending));
1550 return FAILED;
1551 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001552 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 "Timed out waiting for last TM (%d) to complete! \n",
1554 hd->ioc->name, hd->tmPending));
1555 if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
1556 return FAILED;
1557
1558 doTask = 0;
1559 }
1560 } else {
1561 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1562 hd->tmPending |= (1 << type);
1563 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1564 }
1565
1566 /* Is operational?
1567 */
1568 ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
1569
1570#ifdef MPT_DEBUG_RESET
1571 if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
1572 printk(MYIOC_s_WARN_FMT
1573 "TM Handler: IOC Not operational(0x%x)!\n",
1574 hd->ioc->name, ioc_raw_state);
1575 }
1576#endif
1577
1578 if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
1579 && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
1580
1581 /* Isse the Task Mgmt request.
1582 */
1583 if (hd->hard_resets < -1)
1584 hd->hard_resets++;
1585 rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout);
1586 if (rc) {
1587 printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
1588 } else {
1589 dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
1590 }
1591 }
1592
1593 /* Only fall through to the HRH if this is a bus reset
1594 */
1595 if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
1596 ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
1597 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1598 hd->ioc->name));
1599 rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1600 }
1601
1602 dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
1603
1604 return rc;
1605}
1606
1607
1608/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1609/*
1610 * mptscsih_IssueTaskMgmt - Generic send Task Management function.
1611 * @hd: Pointer to MPT_SCSI_HOST structure
1612 * @type: Task Management type
1613 * @target: Logical Target ID for reset (if appropriate)
1614 * @lun: Logical Unit for reset (if appropriate)
1615 * @ctx2abort: Context for the task to be aborted (if appropriate)
1616 *
1617 * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
1618 * or a non-interrupt thread. In the former, must not call schedule().
1619 *
1620 * Not all fields are meaningfull for all task types.
1621 *
1622 * Returns 0 for SUCCESS, -999 for "no msg frames",
1623 * else other non-zero value returned.
1624 */
1625static int
1626mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1627{
1628 MPT_FRAME_HDR *mf;
1629 SCSITaskMgmt_t *pScsiTm;
1630 int ii;
1631 int retval;
1632
1633 /* Return Fail to calling function if no message frames available.
1634 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001635 if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
1637 hd->ioc->name));
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001638 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 }
1640 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
1641 hd->ioc->name, mf));
1642
1643 /* Format the Request
1644 */
1645 pScsiTm = (SCSITaskMgmt_t *) mf;
1646 pScsiTm->TargetID = target;
1647 pScsiTm->Bus = channel;
1648 pScsiTm->ChainOffset = 0;
1649 pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1650
1651 pScsiTm->Reserved = 0;
1652 pScsiTm->TaskType = type;
1653 pScsiTm->Reserved1 = 0;
1654 pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
1655 ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
1656
1657 for (ii= 0; ii < 8; ii++) {
1658 pScsiTm->LUN[ii] = 0;
1659 }
1660 pScsiTm->LUN[1] = lun;
1661
1662 for (ii=0; ii < 7; ii++)
1663 pScsiTm->Reserved2[ii] = 0;
1664
1665 pScsiTm->TaskMsgContext = ctx2abort;
1666
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001667 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n",
1668 hd->ioc->name, ctx2abort, type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm);
1671
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001672 if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 sizeof(SCSITaskMgmt_t), (u32*)pScsiTm,
1674 CAN_SLEEP)) != 0) {
1675 dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!"
1676 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1677 hd->ioc, mf));
1678 mpt_free_msg_frame(hd->ioc, mf);
1679 return retval;
1680 }
1681
1682 if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
1683 dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!"
1684 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1685 hd->ioc, mf));
1686 mpt_free_msg_frame(hd->ioc, mf);
1687 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1688 hd->ioc->name));
1689 retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1690 }
1691
1692 return retval;
1693}
1694
1695/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1696/**
1697 * mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant
1698 * @SCpnt: Pointer to scsi_cmnd structure, IO to be aborted
1699 *
1700 * (linux scsi_host_template.eh_abort_handler routine)
1701 *
1702 * Returns SUCCESS or FAILED.
1703 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001704int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705mptscsih_abort(struct scsi_cmnd * SCpnt)
1706{
1707 MPT_SCSI_HOST *hd;
1708 MPT_ADAPTER *ioc;
1709 MPT_FRAME_HDR *mf;
1710 u32 ctx2abort;
1711 int scpnt_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712
1713 /* If we can't locate our host adapter structure, return FAILED status.
1714 */
1715 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) {
1716 SCpnt->result = DID_RESET << 16;
1717 SCpnt->scsi_done(SCpnt);
1718 dfailprintk((KERN_WARNING MYNAM ": mptscsih_abort: "
1719 "Can't locate host! (sc=%p)\n",
1720 SCpnt));
1721 return FAILED;
1722 }
1723
1724 ioc = hd->ioc;
1725 if (hd->resetPending)
1726 return FAILED;
1727
1728 printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)\n",
1729 hd->ioc->name, SCpnt);
1730
1731 if (hd->timeouts < -1)
1732 hd->timeouts++;
1733
1734 /* Find this command
1735 */
1736 if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
1737 /* Cmd not found in ScsiLookup.
1738 * Do OS callback.
1739 */
1740 SCpnt->result = DID_RESET << 16;
1741 dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
1742 "Command not in the active list! (sc=%p)\n",
1743 hd->ioc->name, SCpnt));
1744 return SUCCESS;
1745 }
1746
1747 /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
1748 * (the IO to be ABORT'd)
1749 *
1750 * NOTE: Since we do not byteswap MsgContext, we do not
1751 * swap it here either. It is an opaque cookie to
1752 * the controller, so it does not matter. -DaveM
1753 */
1754 mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
1755 ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
1756
1757 hd->abortSCpnt = SCpnt;
1758
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759 if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
1760 SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun,
1761 ctx2abort, 2 /* 2 second timeout */)
1762 < 0) {
1763
1764 /* The TM request failed and the subsequent FW-reload failed!
1765 * Fatal error case.
1766 */
1767 printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)\n",
1768 hd->ioc->name, SCpnt);
1769
1770 /* We must clear our pending flag before clearing our state.
1771 */
1772 hd->tmPending = 0;
1773 hd->tmState = TM_STATE_NONE;
1774
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 /* Unmap the DMA buffers, if any. */
1776 if (SCpnt->use_sg) {
1777 pci_unmap_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer,
1778 SCpnt->use_sg, SCpnt->sc_data_direction);
1779 } else if (SCpnt->request_bufflen) {
1780 pci_unmap_single(ioc->pcidev, SCpnt->SCp.dma_handle,
1781 SCpnt->request_bufflen, SCpnt->sc_data_direction);
1782 }
1783 hd->ScsiLookup[scpnt_idx] = NULL;
1784 SCpnt->result = DID_RESET << 16;
1785 SCpnt->scsi_done(SCpnt); /* Issue the command callback */
1786 mptscsih_freeChainBuffers(ioc, scpnt_idx);
1787 mpt_free_msg_frame(ioc, mf);
1788 return FAILED;
1789 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 return SUCCESS;
1791}
1792
1793/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1794/**
1795 * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
1796 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1797 *
1798 * (linux scsi_host_template.eh_dev_reset_handler routine)
1799 *
1800 * Returns SUCCESS or FAILED.
1801 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001802int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
1804{
1805 MPT_SCSI_HOST *hd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
1807 /* If we can't locate our host adapter structure, return FAILED status.
1808 */
1809 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
1810 dtmprintk((KERN_WARNING MYNAM ": mptscsih_dev_reset: "
1811 "Can't locate host! (sc=%p)\n",
1812 SCpnt));
1813 return FAILED;
1814 }
1815
1816 if (hd->resetPending)
1817 return FAILED;
1818
1819 printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n",
1820 hd->ioc->name, SCpnt);
1821
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
1823 SCpnt->device->channel, SCpnt->device->id,
1824 0, 0, 5 /* 5 second timeout */)
1825 < 0){
1826 /* The TM request failed and the subsequent FW-reload failed!
1827 * Fatal error case.
1828 */
1829 printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)\n",
1830 hd->ioc->name, SCpnt);
1831 hd->tmPending = 0;
1832 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 return FAILED;
1834 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835
Jeff Garzik 94d0e7b82005-05-28 07:55:48 -04001836 return SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837}
1838
1839/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1840/**
1841 * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
1842 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1843 *
1844 * (linux scsi_host_template.eh_bus_reset_handler routine)
1845 *
1846 * Returns SUCCESS or FAILED.
1847 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001848int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
1850{
1851 MPT_SCSI_HOST *hd;
1852 spinlock_t *host_lock = SCpnt->device->host->host_lock;
1853
1854 /* If we can't locate our host adapter structure, return FAILED status.
1855 */
1856 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
1857 dtmprintk((KERN_WARNING MYNAM ": mptscsih_bus_reset: "
1858 "Can't locate host! (sc=%p)\n",
1859 SCpnt ) );
1860 return FAILED;
1861 }
1862
1863 printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)\n",
1864 hd->ioc->name, SCpnt);
1865
1866 if (hd->timeouts < -1)
1867 hd->timeouts++;
1868
1869 /* We are now ready to execute the task management request. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
1871 SCpnt->device->channel, 0, 0, 0, 5 /* 5 second timeout */)
1872 < 0){
1873
1874 /* The TM request failed and the subsequent FW-reload failed!
1875 * Fatal error case.
1876 */
1877 printk(MYIOC_s_WARN_FMT
1878 "Error processing TaskMgmt request (sc=%p)\n",
1879 hd->ioc->name, SCpnt);
1880 hd->tmPending = 0;
1881 hd->tmState = TM_STATE_NONE;
1882 spin_lock_irq(host_lock);
1883 return FAILED;
1884 }
Jeff Garzik 68b3aa72005-05-28 07:56:31 -04001885
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 return SUCCESS;
1887}
1888
1889/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1890/**
1891 * mptscsih_host_reset - Perform a SCSI host adapter RESET!
1892 * new_eh variant
1893 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1894 *
1895 * (linux scsi_host_template.eh_host_reset_handler routine)
1896 *
1897 * Returns SUCCESS or FAILED.
1898 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001899int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900mptscsih_host_reset(struct scsi_cmnd *SCpnt)
1901{
1902 MPT_SCSI_HOST * hd;
1903 int status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904
1905 /* If we can't locate the host to reset, then we failed. */
1906 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001907 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 "Can't locate host! (sc=%p)\n",
1909 SCpnt ) );
1910 return FAILED;
1911 }
1912
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001913 printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914 hd->ioc->name, SCpnt);
1915
1916 /* If our attempts to reset the host failed, then return a failed
1917 * status. The host will be taken off line by the SCSI mid-layer.
1918 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
1920 status = FAILED;
1921 } else {
1922 /* Make sure TM pending is cleared and TM state is set to
1923 * NONE.
1924 */
1925 hd->tmPending = 0;
1926 hd->tmState = TM_STATE_NONE;
1927 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001929 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 "Status = %s\n",
1931 (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
1932
1933 return status;
1934}
1935
1936/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1937/**
1938 * mptscsih_tm_pending_wait - wait for pending task management request to
1939 * complete.
1940 * @hd: Pointer to MPT host structure.
1941 *
1942 * Returns {SUCCESS,FAILED}.
1943 */
1944static int
1945mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
1946{
1947 unsigned long flags;
1948 int loop_count = 4 * 10; /* Wait 10 seconds */
1949 int status = FAILED;
1950
1951 do {
1952 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1953 if (hd->tmState == TM_STATE_NONE) {
1954 hd->tmState = TM_STATE_IN_PROGRESS;
1955 hd->tmPending = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001957 status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 break;
1959 }
1960 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1961 msleep(250);
1962 } while (--loop_count);
1963
1964 return status;
1965}
1966
1967/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1968/**
1969 * mptscsih_tm_wait_for_completion - wait for completion of TM task
1970 * @hd: Pointer to MPT host structure.
1971 *
1972 * Returns {SUCCESS,FAILED}.
1973 */
1974static int
1975mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
1976{
1977 unsigned long flags;
1978 int loop_count = 4 * timeout;
1979 int status = FAILED;
1980
1981 do {
1982 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1983 if(hd->tmPending == 0) {
1984 status = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001985 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 break;
1987 }
1988 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1989 msleep_interruptible(250);
1990 } while (--loop_count);
1991
1992 return status;
1993}
1994
1995/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1996/**
1997 * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
1998 * @ioc: Pointer to MPT_ADAPTER structure
1999 * @mf: Pointer to SCSI task mgmt request frame
2000 * @mr: Pointer to SCSI task mgmt reply frame
2001 *
2002 * This routine is called from mptbase.c::mpt_interrupt() at the completion
2003 * of any SCSI task management request.
2004 * This routine is registered with the MPT (base) driver at driver
2005 * load/init time via the mpt_register() API call.
2006 *
2007 * Returns 1 indicating alloc'd request frame ptr should be freed.
2008 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002009int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
2011{
2012 SCSITaskMgmtReply_t *pScsiTmReply;
2013 SCSITaskMgmt_t *pScsiTmReq;
2014 MPT_SCSI_HOST *hd;
2015 unsigned long flags;
2016 u16 iocstatus;
2017 u8 tmType;
2018
2019 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
2020 ioc->name, mf, mr));
2021 if (ioc->sh) {
2022 /* Depending on the thread, a timer is activated for
2023 * the TM request. Delete this timer on completion of TM.
2024 * Decrement count of outstanding TM requests.
2025 */
2026 hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
2027 } else {
2028 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
2029 ioc->name));
2030 return 1;
2031 }
2032
2033 if (mr == NULL) {
2034 dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
2035 ioc->name, mf));
2036 return 1;
2037 } else {
2038 pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
2039 pScsiTmReq = (SCSITaskMgmt_t*)mf;
2040
2041 /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
2042 tmType = pScsiTmReq->TaskType;
2043
2044 dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n",
2045 ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
2046 DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply);
2047
2048 iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2049 dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n",
2050 ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo)));
2051 /* Error? (anything non-zero?) */
2052 if (iocstatus) {
2053
2054 /* clear flags and continue.
2055 */
2056 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
2057 hd->abortSCpnt = NULL;
2058
2059 /* If an internal command is present
2060 * or the TM failed - reload the FW.
2061 * FC FW may respond FAILED to an ABORT
2062 */
2063 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
2064 if ((hd->cmdPtr) ||
2065 (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
2066 if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
2067 printk((KERN_WARNING
2068 " Firmware Reload FAILED!!\n"));
2069 }
2070 }
2071 }
2072 } else {
2073 dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name));
2074
2075 hd->abortSCpnt = NULL;
2076
2077 }
2078 }
2079
2080 spin_lock_irqsave(&ioc->FreeQlock, flags);
2081 hd->tmPending = 0;
2082 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2083 hd->tmState = TM_STATE_NONE;
2084
2085 return 1;
2086}
2087
2088/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2089/*
2090 * This is anyones guess quite frankly.
2091 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002092int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
2094 sector_t capacity, int geom[])
2095{
2096 int heads;
2097 int sectors;
2098 sector_t cylinders;
2099 ulong dummy;
2100
2101 heads = 64;
2102 sectors = 32;
2103
2104 dummy = heads * sectors;
2105 cylinders = capacity;
2106 sector_div(cylinders,dummy);
2107
2108 /*
2109 * Handle extended translation size for logical drives
2110 * > 1Gb
2111 */
2112 if ((ulong)capacity >= 0x200000) {
2113 heads = 255;
2114 sectors = 63;
2115 dummy = heads * sectors;
2116 cylinders = capacity;
2117 sector_div(cylinders,dummy);
2118 }
2119
2120 /* return result */
2121 geom[0] = heads;
2122 geom[1] = sectors;
2123 geom[2] = cylinders;
2124
2125 dprintk((KERN_NOTICE
2126 ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n",
2127 sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors));
2128
2129 return 0;
2130}
2131
2132/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2133/*
2134 * OS entry point to allow host driver to alloc memory
2135 * for each scsi device. Called once per device the bus scan.
2136 * Return non-zero if allocation fails.
2137 * Init memory once per id (not LUN).
2138 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002139int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140mptscsih_slave_alloc(struct scsi_device *device)
2141{
2142 struct Scsi_Host *host = device->host;
2143 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
2144 VirtDevice *vdev;
2145 uint target = device->id;
2146
2147 if (hd == NULL)
2148 return -ENODEV;
2149
2150 if ((vdev = hd->Targets[target]) != NULL)
2151 goto out;
2152
2153 vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL);
2154 if (!vdev) {
2155 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
2156 hd->ioc->name, sizeof(VirtDevice));
2157 return -ENOMEM;
2158 }
2159
2160 memset(vdev, 0, sizeof(VirtDevice));
2161 vdev->tflags = MPT_TARGET_FLAGS_Q_YES;
2162 vdev->ioc_id = hd->ioc->id;
2163 vdev->target_id = device->id;
2164 vdev->bus_id = device->channel;
2165 vdev->raidVolume = 0;
2166 hd->Targets[device->id] = vdev;
2167 if (hd->ioc->bus_type == SCSI) {
2168 if (hd->ioc->spi_data.isRaid & (1 << device->id)) {
2169 vdev->raidVolume = 1;
2170 ddvtprintk((KERN_INFO
2171 "RAID Volume @ id %d\n", device->id));
2172 }
2173 } else {
2174 vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2175 }
2176
2177 out:
2178 vdev->num_luns++;
Christoph Hellwig82ffb6712005-09-09 16:25:54 +02002179 device->hostdata = vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 return 0;
2181}
2182
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002183static int
2184mptscsih_is_raid_volume(MPT_SCSI_HOST *hd, uint id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185{
2186 int i;
2187
2188 if (!hd->ioc->spi_data.isRaid || !hd->ioc->spi_data.pIocPg3)
2189 return 0;
2190
2191 for (i = 0; i < hd->ioc->spi_data.pIocPg3->NumPhysDisks; i++) {
2192 if (id == hd->ioc->spi_data.pIocPg3->PhysDisk[i].PhysDiskID)
2193 return 1;
2194 }
2195
2196 return 0;
2197}
2198
2199/*
2200 * OS entry point to allow for host driver to free allocated memory
2201 * Called if no device present or device being unloaded
2202 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002203void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204mptscsih_slave_destroy(struct scsi_device *device)
2205{
2206 struct Scsi_Host *host = device->host;
2207 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
2208 VirtDevice *vdev;
2209 uint target = device->id;
2210 uint lun = device->lun;
2211
2212 if (hd == NULL)
2213 return;
2214
2215 mptscsih_search_running_cmds(hd, target, lun);
2216
2217 vdev = hd->Targets[target];
2218 vdev->luns[0] &= ~(1 << lun);
2219 if (--vdev->num_luns)
2220 return;
2221
2222 kfree(hd->Targets[target]);
2223 hd->Targets[target] = NULL;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002224
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225 if (hd->ioc->bus_type == SCSI) {
2226 if (mptscsih_is_raid_volume(hd, target)) {
2227 hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3;
2228 } else {
2229 hd->ioc->spi_data.dvStatus[target] =
2230 MPT_SCSICFG_NEGOTIATE;
2231
2232 if (!hd->negoNvram) {
2233 hd->ioc->spi_data.dvStatus[target] |=
2234 MPT_SCSICFG_DV_NOT_DONE;
2235 }
2236 }
2237 }
2238}
2239
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002240/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2241/*
2242 * mptscsih_change_queue_depth - This function will set a devices queue depth
2243 * @sdev: per scsi_device pointer
2244 * @qdepth: requested queue depth
2245 *
2246 * Adding support for new 'change_queue_depth' api.
2247*/
2248int
2249mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250{
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002251 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
2252 VirtDevice *pTarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 int max_depth;
2254 int tagged;
2255
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002256 if (hd == NULL)
2257 return 0;
2258 if (!(pTarget = hd->Targets[sdev->id]))
2259 return 0;
2260
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 if (hd->ioc->bus_type == SCSI) {
2262 if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) {
2263 if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES))
2264 max_depth = 1;
2265 else if (((pTarget->inq_data[0] & 0x1f) == 0x00) &&
2266 (pTarget->minSyncFactor <= MPT_ULTRA160 ))
2267 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2268 else
2269 max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
2270 } else {
2271 /* error case - No Inq. Data */
2272 max_depth = 1;
2273 }
2274 } else
2275 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2276
2277 if (qdepth > max_depth)
2278 qdepth = max_depth;
2279 if (qdepth == 1)
2280 tagged = 0;
2281 else
2282 tagged = MSG_SIMPLE_TAG;
2283
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002284 scsi_adjust_queue_depth(sdev, tagged, qdepth);
2285 return sdev->queue_depth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286}
2287
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288/*
2289 * OS entry point to adjust the queue_depths on a per-device basis.
2290 * Called once per device the bus scan. Use it to force the queue_depth
2291 * member to 1 if a device does not support Q tags.
2292 * Return non-zero if fails.
2293 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002294int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295mptscsih_slave_configure(struct scsi_device *device)
2296{
2297 struct Scsi_Host *sh = device->host;
2298 VirtDevice *pTarget;
2299 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sh->hostdata;
2300
2301 if ((hd == NULL) || (hd->Targets == NULL)) {
2302 return 0;
2303 }
2304
2305 dsprintk((MYIOC_s_INFO_FMT
2306 "device @ %p, id=%d, LUN=%d, channel=%d\n",
2307 hd->ioc->name, device, device->id, device->lun, device->channel));
2308 dsprintk((MYIOC_s_INFO_FMT
2309 "sdtr %d wdtr %d ppr %d inq length=%d\n",
2310 hd->ioc->name, device->sdtr, device->wdtr,
2311 device->ppr, device->inquiry_len));
2312
2313 if (device->id > sh->max_id) {
2314 /* error case, should never happen */
2315 scsi_adjust_queue_depth(device, 0, 1);
2316 goto slave_configure_exit;
2317 }
2318
2319 pTarget = hd->Targets[device->id];
2320
2321 if (pTarget == NULL) {
2322 /* Driver doesn't know about this device.
2323 * Kernel may generate a "Dummy Lun 0" which
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002324 * may become a real Lun if a
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 * "scsi add-single-device" command is executed
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002326 * while the driver is active (hot-plug a
2327 * device). LSI Raid controllers need
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 * queue_depth set to DEV_HIGH for this reason.
2329 */
2330 scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
2331 MPT_SCSI_CMD_PER_DEV_HIGH);
2332 goto slave_configure_exit;
2333 }
2334
2335 mptscsih_initTarget(hd, device->channel, device->id, device->lun,
2336 device->inquiry, device->inquiry_len );
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002337 mptscsih_change_queue_depth(device, MPT_SCSI_CMD_PER_DEV_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
2339 dsprintk((MYIOC_s_INFO_FMT
2340 "Queue depth=%d, tflags=%x\n",
2341 hd->ioc->name, device->queue_depth, pTarget->tflags));
2342
2343 dsprintk((MYIOC_s_INFO_FMT
2344 "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n",
2345 hd->ioc->name, pTarget->negoFlags, pTarget->maxOffset, pTarget->minSyncFactor));
2346
2347slave_configure_exit:
2348
2349 dsprintk((MYIOC_s_INFO_FMT
2350 "tagged %d, simple %d, ordered %d\n",
2351 hd->ioc->name,device->tagged_supported, device->simple_tags,
2352 device->ordered_tags));
2353
2354 return 0;
2355}
2356
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2358/*
2359 * Private routines...
2360 */
2361
2362/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2363/* Utility function to copy sense data from the scsi_cmnd buffer
2364 * to the FC and SCSI target structures.
2365 *
2366 */
2367static void
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002368mptscsih_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 -07002369{
2370 VirtDevice *target;
2371 SCSIIORequest_t *pReq;
2372 u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
2373 int index;
2374
2375 /* Get target structure
2376 */
2377 pReq = (SCSIIORequest_t *) mf;
2378 index = (int) pReq->TargetID;
2379 target = hd->Targets[index];
2380
2381 if (sense_count) {
2382 u8 *sense_data;
2383 int req_index;
2384
2385 /* Copy the sense received into the scsi command block. */
2386 req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2387 sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
2388 memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
2389
2390 /* Log SMART data (asc = 0x5D, non-IM case only) if required.
2391 */
2392 if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
2393 if ((sense_data[12] == 0x5D) && (target->raidVolume == 0)) {
2394 int idx;
2395 MPT_ADAPTER *ioc = hd->ioc;
2396
2397 idx = ioc->eventContext % ioc->eventLogSize;
2398 ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
2399 ioc->events[idx].eventContext = ioc->eventContext;
2400
2401 ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
2402 (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
2403 (pReq->Bus << 8) || pReq->TargetID;
2404
2405 ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
2406
2407 ioc->eventContext++;
2408 }
2409 }
2410 } else {
2411 dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
2412 hd->ioc->name));
2413 }
2414}
2415
2416static u32
2417SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
2418{
2419 MPT_SCSI_HOST *hd;
2420 int i;
2421
2422 hd = (MPT_SCSI_HOST *) sc->device->host->hostdata;
2423
2424 for (i = 0; i < hd->ioc->req_depth; i++) {
2425 if (hd->ScsiLookup[i] == sc) {
2426 return i;
2427 }
2428 }
2429
2430 return -1;
2431}
2432
2433/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002434int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
2436{
2437 MPT_SCSI_HOST *hd;
2438 unsigned long flags;
2439
2440 dtmprintk((KERN_WARNING MYNAM
2441 ": IOC %s_reset routed to SCSI host driver!\n",
2442 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
2443 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
2444
2445 /* If a FW reload request arrives after base installed but
2446 * before all scsi hosts have been attached, then an alt_ioc
2447 * may have a NULL sh pointer.
2448 */
2449 if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
2450 return 0;
2451 else
2452 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2453
2454 if (reset_phase == MPT_IOC_SETUP_RESET) {
2455 dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
2456
2457 /* Clean Up:
2458 * 1. Set Hard Reset Pending Flag
2459 * All new commands go to doneQ
2460 */
2461 hd->resetPending = 1;
2462
2463 } else if (reset_phase == MPT_IOC_PRE_RESET) {
2464 dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
2465
2466 /* 2. Flush running commands
2467 * Clean ScsiLookup (and associated memory)
2468 * AND clean mytaskQ
2469 */
2470
2471 /* 2b. Reply to OS all known outstanding I/O commands.
2472 */
2473 mptscsih_flush_running_cmds(hd);
2474
2475 /* 2c. If there was an internal command that
2476 * has not completed, configuration or io request,
2477 * free these resources.
2478 */
2479 if (hd->cmdPtr) {
2480 del_timer(&hd->timer);
2481 mpt_free_msg_frame(ioc, hd->cmdPtr);
2482 }
2483
2484 dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
2485
2486 } else {
2487 dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
2488
2489 /* Once a FW reload begins, all new OS commands are
2490 * redirected to the doneQ w/ a reset status.
2491 * Init all control structures.
2492 */
2493
2494 /* ScsiLookup initialization
2495 */
2496 {
2497 int ii;
2498 for (ii=0; ii < hd->ioc->req_depth; ii++)
2499 hd->ScsiLookup[ii] = NULL;
2500 }
2501
2502 /* 2. Chain Buffer initialization
2503 */
2504
2505 /* 4. Renegotiate to all devices, if SCSI
2506 */
2507 if (ioc->bus_type == SCSI) {
2508 dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n"));
2509 mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM);
2510 }
2511
2512 /* 5. Enable new commands to be posted
2513 */
2514 spin_lock_irqsave(&ioc->FreeQlock, flags);
2515 hd->tmPending = 0;
2516 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2517 hd->resetPending = 0;
2518 hd->tmState = TM_STATE_NONE;
2519
2520 /* 6. If there was an internal command,
2521 * wake this process up.
2522 */
2523 if (hd->cmdPtr) {
2524 /*
2525 * Wake up the original calling thread
2526 */
2527 hd->pLocal = &hd->localReply;
2528 hd->pLocal->completion = MPT_SCANDV_DID_RESET;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002529 hd->scandv_wait_done = 1;
2530 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 hd->cmdPtr = NULL;
2532 }
2533
2534 /* 7. Set flag to force DV and re-read IOC Page 3
2535 */
2536 if (ioc->bus_type == SCSI) {
2537 ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
2538 ddvtprintk(("Set reload IOC Pg3 Flag\n"));
2539 }
2540
2541 dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
2542
2543 }
2544
2545 return 1; /* currently means nothing really */
2546}
2547
2548/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002549int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
2551{
2552 MPT_SCSI_HOST *hd;
2553 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
2554
2555 devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
2556 ioc->name, event));
2557
2558 switch (event) {
2559 case MPI_EVENT_UNIT_ATTENTION: /* 03 */
2560 /* FIXME! */
2561 break;
2562 case MPI_EVENT_IOC_BUS_RESET: /* 04 */
2563 case MPI_EVENT_EXT_BUS_RESET: /* 05 */
2564 hd = NULL;
2565 if (ioc->sh) {
2566 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2567 if (hd && (ioc->bus_type == SCSI) && (hd->soft_resets < -1))
2568 hd->soft_resets++;
2569 }
2570 break;
2571 case MPI_EVENT_LOGOUT: /* 09 */
2572 /* FIXME! */
2573 break;
2574
2575 /*
2576 * CHECKME! Don't think we need to do
2577 * anything for these, but...
2578 */
2579 case MPI_EVENT_RESCAN: /* 06 */
2580 case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
2581 case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
2582 /*
2583 * CHECKME! Falling thru...
2584 */
2585 break;
2586
2587 case MPI_EVENT_INTEGRATED_RAID: /* 0B */
2588#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
2589 /* negoNvram set to 0 if DV enabled and to USE_NVRAM if
2590 * if DV disabled. Need to check for target mode.
2591 */
2592 hd = NULL;
2593 if (ioc->sh)
2594 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2595
2596 if (hd && (ioc->bus_type == SCSI) && (hd->negoNvram == 0)) {
2597 ScsiCfgData *pSpi;
2598 Ioc3PhysDisk_t *pPDisk;
2599 int numPDisk;
2600 u8 reason;
2601 u8 physDiskNum;
2602
2603 reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16;
2604 if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) {
2605 /* New or replaced disk.
2606 * Set DV flag and schedule DV.
2607 */
2608 pSpi = &ioc->spi_data;
2609 physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24;
2610 ddvtprintk(("DV requested for phys disk id %d\n", physDiskNum));
2611 if (pSpi->pIocPg3) {
2612 pPDisk = pSpi->pIocPg3->PhysDisk;
2613 numPDisk =pSpi->pIocPg3->NumPhysDisks;
2614
2615 while (numPDisk) {
2616 if (physDiskNum == pPDisk->PhysDiskNum) {
2617 pSpi->dvStatus[pPDisk->PhysDiskID] = (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE);
2618 pSpi->forceDv = MPT_SCSICFG_NEED_DV;
2619 ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
2620 break;
2621 }
2622 pPDisk++;
2623 numPDisk--;
2624 }
2625
2626 if (numPDisk == 0) {
2627 /* The physical disk that needs DV was not found
2628 * in the stored IOC Page 3. The driver must reload
2629 * this page. DV routine will set the NEED_DV flag for
2630 * all phys disks that have DV_NOT_DONE set.
2631 */
2632 pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
2633 ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n", physDiskNum));
2634 }
2635 }
2636 }
2637 }
2638#endif
2639
2640#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
2641 printk("Raid Event RF: ");
2642 {
2643 u32 *m = (u32 *)pEvReply;
2644 int ii;
2645 int n = (int)pEvReply->MsgLength;
2646 for (ii=6; ii < n; ii++)
2647 printk(" %08x", le32_to_cpu(m[ii]));
2648 printk("\n");
2649 }
2650#endif
2651 break;
2652
2653 case MPI_EVENT_NONE: /* 00 */
2654 case MPI_EVENT_LOG_DATA: /* 01 */
2655 case MPI_EVENT_STATE_CHANGE: /* 02 */
2656 case MPI_EVENT_EVENT_CHANGE: /* 0A */
2657 default:
2658 dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
2659 break;
2660 }
2661
2662 return 1; /* currently means nothing really */
2663}
2664
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2666/*
2667 * mptscsih_initTarget - Target, LUN alloc/free functionality.
2668 * @hd: Pointer to MPT_SCSI_HOST structure
2669 * @bus_id: Bus number (?)
2670 * @target_id: SCSI target id
2671 * @lun: SCSI LUN id
2672 * @data: Pointer to data
2673 * @dlen: Number of INQUIRY bytes
2674 *
2675 * NOTE: It's only SAFE to call this routine if data points to
2676 * sane & valid STANDARD INQUIRY data!
2677 *
2678 * Allocate and initialize memory for this target.
2679 * Save inquiry data.
2680 *
2681 */
2682static void
2683mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen)
2684{
2685 int indexed_lun, lun_index;
2686 VirtDevice *vdev;
2687 ScsiCfgData *pSpi;
2688 char data_56;
2689
2690 dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n",
2691 hd->ioc->name, bus_id, target_id, lun, hd));
2692
2693 /*
2694 * If the peripheral qualifier filter is enabled then if the target reports a 0x1
2695 * (i.e. The targer is capable of supporting the specified peripheral device type
2696 * on this logical unit; however, the physical device is not currently connected
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002697 * to this logical unit) it will be converted to a 0x3 (i.e. The target is not
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 * capable of supporting a physical device on this logical unit). This is to work
2699 * around a bug in th emid-layer in some distributions in which the mid-layer will
2700 * continue to try to communicate to the LUN and evntually create a dummy LUN.
2701 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002702 if (hd->mpt_pq_filter && dlen && (data[0] & 0xE0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 data[0] |= 0x40;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002704
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 /* Is LUN supported? If so, upper 2 bits will be 0
2706 * in first byte of inquiry data.
2707 */
2708 if (data[0] & 0xe0)
2709 return;
2710
2711 if ((vdev = hd->Targets[target_id]) == NULL) {
2712 return;
2713 }
2714
2715 lun_index = (lun >> 5); /* 32 luns per lun_index */
2716 indexed_lun = (lun % 32);
2717 vdev->luns[lun_index] |= (1 << indexed_lun);
2718
2719 if (hd->ioc->bus_type == SCSI) {
2720 if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) {
2721 /* Treat all Processors as SAF-TE if
2722 * command line option is set */
2723 vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2724 mptscsih_writeIOCPage4(hd, target_id, bus_id);
2725 }else if ((data[0] == TYPE_PROCESSOR) &&
2726 !(vdev->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) {
2727 if ( dlen > 49 ) {
2728 vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2729 if ( data[44] == 'S' &&
2730 data[45] == 'A' &&
2731 data[46] == 'F' &&
2732 data[47] == '-' &&
2733 data[48] == 'T' &&
2734 data[49] == 'E' ) {
2735 vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2736 mptscsih_writeIOCPage4(hd, target_id, bus_id);
2737 }
2738 }
2739 }
2740 if (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) {
2741 if ( dlen > 8 ) {
2742 memcpy (vdev->inq_data, data, 8);
2743 } else {
2744 memcpy (vdev->inq_data, data, dlen);
2745 }
2746
2747 /* If have not done DV, set the DV flag.
2748 */
2749 pSpi = &hd->ioc->spi_data;
2750 if ((data[0] == TYPE_TAPE) || (data[0] == TYPE_PROCESSOR)) {
2751 if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE)
2752 pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV;
2753 }
2754
2755 vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2756
2757
2758 data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */
2759 if (dlen > 56) {
2760 if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2761 /* Update the target capabilities
2762 */
2763 data_56 = data[56];
2764 vdev->tflags |= MPT_TARGET_FLAGS_VALID_56;
2765 }
2766 }
2767 mptscsih_setTargetNegoParms(hd, vdev, data_56);
2768 } else {
2769 /* Initial Inquiry may not request enough data bytes to
2770 * obtain byte 57. DV will; if target doesn't return
2771 * at least 57 bytes, data[56] will be zero. */
2772 if (dlen > 56) {
2773 if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2774 /* Update the target capabilities
2775 */
2776 data_56 = data[56];
2777 vdev->tflags |= MPT_TARGET_FLAGS_VALID_56;
2778 mptscsih_setTargetNegoParms(hd, vdev, data_56);
2779 }
2780 }
2781 }
2782 }
2783}
2784
2785/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2786/*
2787 * Update the target negotiation parameters based on the
2788 * the Inquiry data, adapter capabilities, and NVRAM settings.
2789 *
2790 */
2791static void
2792mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56)
2793{
2794 ScsiCfgData *pspi_data = &hd->ioc->spi_data;
2795 int id = (int) target->target_id;
2796 int nvram;
2797 VirtDevice *vdev;
2798 int ii;
2799 u8 width = MPT_NARROW;
2800 u8 factor = MPT_ASYNC;
2801 u8 offset = 0;
2802 u8 version, nfactor;
2803 u8 noQas = 1;
2804
2805 target->negoFlags = pspi_data->noQas;
2806
2807 /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine
2808 * support. If available, default QAS to off and allow enabling.
2809 * If not available, default QAS to on, turn off for non-disks.
2810 */
2811
2812 /* Set flags based on Inquiry data
2813 */
2814 version = target->inq_data[2] & 0x07;
2815 if (version < 2) {
2816 width = 0;
2817 factor = MPT_ULTRA2;
2818 offset = pspi_data->maxSyncOffset;
2819 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2820 } else {
2821 if (target->inq_data[7] & 0x20) {
2822 width = 1;
2823 }
2824
2825 if (target->inq_data[7] & 0x10) {
2826 factor = pspi_data->minSyncFactor;
2827 if (target->tflags & MPT_TARGET_FLAGS_VALID_56) {
2828 /* bits 2 & 3 show Clocking support */
2829 if ((byte56 & 0x0C) == 0)
2830 factor = MPT_ULTRA2;
2831 else {
2832 if ((byte56 & 0x03) == 0)
2833 factor = MPT_ULTRA160;
2834 else {
2835 factor = MPT_ULTRA320;
2836 if (byte56 & 0x02)
2837 {
2838 ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id));
2839 noQas = 0;
2840 }
2841 if (target->inq_data[0] == TYPE_TAPE) {
2842 if (byte56 & 0x01)
2843 target->negoFlags |= MPT_TAPE_NEGO_IDP;
2844 }
2845 }
2846 }
2847 } else {
2848 ddvtprintk((KERN_INFO "Enabling QAS on id=%d due to ~TARGET_FLAGS_VALID_56!\n", id));
2849 noQas = 0;
2850 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002851
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 offset = pspi_data->maxSyncOffset;
2853
2854 /* If RAID, never disable QAS
2855 * else if non RAID, do not disable
2856 * QAS if bit 1 is set
2857 * bit 1 QAS support, non-raid only
2858 * bit 0 IU support
2859 */
2860 if (target->raidVolume == 1) {
2861 noQas = 0;
2862 }
2863 } else {
2864 factor = MPT_ASYNC;
2865 offset = 0;
2866 }
2867 }
2868
2869 if ( (target->inq_data[7] & 0x02) == 0) {
2870 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2871 }
2872
2873 /* Update tflags based on NVRAM settings. (SCSI only)
2874 */
2875 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
2876 nvram = pspi_data->nvram[id];
2877 nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
2878
2879 if (width)
2880 width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
2881
2882 if (offset > 0) {
2883 /* Ensure factor is set to the
2884 * maximum of: adapter, nvram, inquiry
2885 */
2886 if (nfactor) {
2887 if (nfactor < pspi_data->minSyncFactor )
2888 nfactor = pspi_data->minSyncFactor;
2889
2890 factor = max(factor, nfactor);
2891 if (factor == MPT_ASYNC)
2892 offset = 0;
2893 } else {
2894 offset = 0;
2895 factor = MPT_ASYNC;
2896 }
2897 } else {
2898 factor = MPT_ASYNC;
2899 }
2900 }
2901
2902 /* Make sure data is consistent
2903 */
2904 if ((!width) && (factor < MPT_ULTRA2)) {
2905 factor = MPT_ULTRA2;
2906 }
2907
2908 /* Save the data to the target structure.
2909 */
2910 target->minSyncFactor = factor;
2911 target->maxOffset = offset;
2912 target->maxWidth = width;
2913
2914 target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
2915
2916 /* Disable unused features.
2917 */
2918 if (!width)
2919 target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
2920
2921 if (!offset)
2922 target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
2923
2924 if ( factor > MPT_ULTRA320 )
2925 noQas = 0;
2926
2927 /* GEM, processor WORKAROUND
2928 */
2929 if ((target->inq_data[0] == TYPE_PROCESSOR) || (target->inq_data[0] > 0x08)) {
2930 target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC);
2931 pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO;
2932 } else {
2933 if (noQas && (pspi_data->noQas == 0)) {
2934 pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS;
2935 target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2936
2937 /* Disable QAS in a mixed configuration case
2938 */
2939
2940 ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id));
2941 for (ii = 0; ii < id; ii++) {
2942 if ( (vdev = hd->Targets[ii]) ) {
2943 vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2944 mptscsih_writeSDP1(hd, 0, ii, vdev->negoFlags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002945 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 }
2947 }
2948 }
2949
2950 /* Write SDP1 on this I/O to this target */
2951 if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) {
2952 ddvtprintk((KERN_INFO "MPT_SCSICFG_NEGOTIATE on id=%d!\n", id));
2953 mptscsih_writeSDP1(hd, 0, id, hd->negoNvram);
2954 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE;
2955 } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) {
2956 ddvtprintk((KERN_INFO "MPT_SCSICFG_BLK_NEGO on id=%d!\n", id));
2957 mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO);
2958 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO;
2959 }
2960}
2961
2962/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2963/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
2964 * Else set the NEED_DV flag after Read Capacity Issued (disks)
2965 * or Mode Sense (cdroms).
2966 *
2967 * Tapes, initTarget will set this flag on completion of Inquiry command.
2968 * Called only if DV_NOT_DONE flag is set
2969 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002970static void
2971mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972{
2973 u8 cmd;
2974 ScsiCfgData *pSpi;
2975
2976 ddvtprintk((" set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n",
2977 pReq->TargetID, pReq->LUN[1], hd->negoNvram, pReq->CDB[0]));
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002978
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 if ((pReq->LUN[1] != 0) || (hd->negoNvram != 0))
2980 return;
2981
2982 cmd = pReq->CDB[0];
2983
2984 if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) {
2985 pSpi = &hd->ioc->spi_data;
2986 if ((pSpi->isRaid & (1 << pReq->TargetID)) && pSpi->pIocPg3) {
2987 /* Set NEED_DV for all hidden disks
2988 */
2989 Ioc3PhysDisk_t *pPDisk = pSpi->pIocPg3->PhysDisk;
2990 int numPDisk = pSpi->pIocPg3->NumPhysDisks;
2991
2992 while (numPDisk) {
2993 pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
2994 ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
2995 pPDisk++;
2996 numPDisk--;
2997 }
2998 }
2999 pSpi->dvStatus[pReq->TargetID] |= MPT_SCSICFG_NEED_DV;
3000 ddvtprintk(("NEED_DV set for visible disk id %d\n", pReq->TargetID));
3001 }
3002}
3003
3004/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3005/*
3006 * If no Target, bus reset on 1st I/O. Set the flag to
3007 * prevent any future negotiations to this device.
3008 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003009static void
3010mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011{
3012
3013 if ((hd->Targets) && (hd->Targets[target_id] == NULL))
3014 hd->ioc->spi_data.dvStatus[target_id] |= MPT_SCSICFG_BLK_NEGO;
3015
3016 return;
3017}
3018
3019/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3020/*
3021 * SCSI Config Page functionality ...
3022 */
3023/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3024/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags
3025 * based on width, factor and offset parameters.
3026 * @width: bus width
3027 * @factor: sync factor
3028 * @offset: sync offset
3029 * @requestedPtr: pointer to requested values (updated)
3030 * @configurationPtr: pointer to configuration values (updated)
3031 * @flags: flags to block WDTR or SDTR negotiation
3032 *
3033 * Return: None.
3034 *
3035 * Remark: Called by writeSDP1 and _dv_params
3036 */
3037static void
3038mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags)
3039{
3040 u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE;
3041 u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC;
3042
3043 *configurationPtr = 0;
3044 *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0;
3045 *requestedPtr |= (offset << 16) | (factor << 8);
3046
3047 if (width && offset && !nowide && !nosync) {
3048 if (factor < MPT_ULTRA160) {
3049 *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT);
3050 if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0)
3051 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS;
3052 if (flags & MPT_TAPE_NEGO_IDP)
3053 *requestedPtr |= 0x08000000;
3054 } else if (factor < MPT_ULTRA2) {
3055 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT;
3056 }
3057 }
3058
3059 if (nowide)
3060 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED;
3061
3062 if (nosync)
3063 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED;
3064
3065 return;
3066}
3067
3068/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3069/* mptscsih_writeSDP1 - write SCSI Device Page 1
3070 * @hd: Pointer to a SCSI Host Strucutre
3071 * @portnum: IOC port number
3072 * @target_id: writeSDP1 for single ID
3073 * @flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO
3074 *
3075 * Return: -EFAULT if read of config page header fails
3076 * or 0 if success.
3077 *
3078 * Remark: If a target has been found, the settings from the
3079 * target structure are used, else the device is set
3080 * to async/narrow.
3081 *
3082 * Remark: Called during init and after a FW reload.
3083 * Remark: We do not wait for a return, write pages sequentially.
3084 */
3085static int
3086mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags)
3087{
3088 MPT_ADAPTER *ioc = hd->ioc;
3089 Config_t *pReq;
3090 SCSIDevicePage1_t *pData;
3091 VirtDevice *pTarget;
3092 MPT_FRAME_HDR *mf;
3093 dma_addr_t dataDma;
3094 u16 req_idx;
3095 u32 frameOffset;
3096 u32 requested, configuration, flagsLength;
3097 int ii, nvram;
3098 int id = 0, maxid = 0;
3099 u8 width;
3100 u8 factor;
3101 u8 offset;
3102 u8 bus = 0;
3103 u8 negoFlags;
3104 u8 maxwidth, maxoffset, maxfactor;
3105
3106 if (ioc->spi_data.sdp1length == 0)
3107 return 0;
3108
3109 if (flags & MPT_SCSICFG_ALL_IDS) {
3110 id = 0;
3111 maxid = ioc->sh->max_id - 1;
3112 } else if (ioc->sh) {
3113 id = target_id;
3114 maxid = min_t(int, id, ioc->sh->max_id - 1);
3115 }
3116
3117 for (; id <= maxid; id++) {
3118
3119 if (id == ioc->pfacts[portnum].PortSCSIID)
3120 continue;
3121
3122 /* Use NVRAM to get adapter and target maximums
3123 * Data over-riden by target structure information, if present
3124 */
3125 maxwidth = ioc->spi_data.maxBusWidth;
3126 maxoffset = ioc->spi_data.maxSyncOffset;
3127 maxfactor = ioc->spi_data.minSyncFactor;
3128 if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3129 nvram = ioc->spi_data.nvram[id];
3130
3131 if (maxwidth)
3132 maxwidth = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
3133
3134 if (maxoffset > 0) {
3135 maxfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
3136 if (maxfactor == 0) {
3137 /* Key for async */
3138 maxfactor = MPT_ASYNC;
3139 maxoffset = 0;
3140 } else if (maxfactor < ioc->spi_data.minSyncFactor) {
3141 maxfactor = ioc->spi_data.minSyncFactor;
3142 }
3143 } else
3144 maxfactor = MPT_ASYNC;
3145 }
3146
3147 /* Set the negotiation flags.
3148 */
3149 negoFlags = ioc->spi_data.noQas;
3150 if (!maxwidth)
3151 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
3152
3153 if (!maxoffset)
3154 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
3155
3156 if (flags & MPT_SCSICFG_USE_NVRAM) {
3157 width = maxwidth;
3158 factor = maxfactor;
3159 offset = maxoffset;
3160 } else {
3161 width = 0;
3162 factor = MPT_ASYNC;
3163 offset = 0;
3164 //negoFlags = 0;
3165 //negoFlags = MPT_TARGET_NO_NEGO_SYNC;
3166 }
3167
3168 /* If id is not a raid volume, get the updated
3169 * transmission settings from the target structure.
3170 */
3171 if (hd->Targets && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) {
3172 width = pTarget->maxWidth;
3173 factor = pTarget->minSyncFactor;
3174 offset = pTarget->maxOffset;
3175 negoFlags = pTarget->negoFlags;
3176 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003177
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3179 /* Force to async and narrow if DV has not been executed
3180 * for this ID
3181 */
3182 if ((hd->ioc->spi_data.dvStatus[id] & MPT_SCSICFG_DV_NOT_DONE) != 0) {
3183 width = 0;
3184 factor = MPT_ASYNC;
3185 offset = 0;
3186 }
3187#endif
3188
3189 if (flags & MPT_SCSICFG_BLK_NEGO)
3190 negoFlags = MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC;
3191
3192 mptscsih_setDevicePage1Flags(width, factor, offset,
3193 &requested, &configuration, negoFlags);
3194 dnegoprintk(("writeSDP1: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
3195 target_id, width, factor, offset, negoFlags, requested, configuration));
3196
3197 /* Get a MF for this command.
3198 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003199 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003200 dfailprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n",
3201 ioc->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202 return -EAGAIN;
3203 }
3204
3205 ddvprintk((MYIOC_s_INFO_FMT "WriteSDP1 (mf=%p, id=%d, req=0x%x, cfg=0x%x)\n",
3206 hd->ioc->name, mf, id, requested, configuration));
3207
3208
3209 /* Set the request and the data pointers.
3210 * Request takes: 36 bytes (32 bit SGE)
3211 * SCSI Device Page 1 requires 16 bytes
3212 * 40 + 16 <= size of SCSI IO Request = 56 bytes
3213 * and MF size >= 64 bytes.
3214 * Place data at end of MF.
3215 */
3216 pReq = (Config_t *)mf;
3217
3218 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3219 frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t);
3220
3221 pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset);
3222 dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset;
3223
3224 /* Complete the request frame (same for all requests).
3225 */
3226 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3227 pReq->Reserved = 0;
3228 pReq->ChainOffset = 0;
3229 pReq->Function = MPI_FUNCTION_CONFIG;
3230 pReq->ExtPageLength = 0;
3231 pReq->ExtPageType = 0;
3232 pReq->MsgFlags = 0;
3233 for (ii=0; ii < 8; ii++) {
3234 pReq->Reserved2[ii] = 0;
3235 }
3236 pReq->Header.PageVersion = ioc->spi_data.sdp1version;
3237 pReq->Header.PageLength = ioc->spi_data.sdp1length;
3238 pReq->Header.PageNumber = 1;
3239 pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
3240 pReq->PageAddress = cpu_to_le32(id | (bus << 8 ));
3241
3242 /* Add a SGE to the config request.
3243 */
3244 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4;
3245
3246 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3247
3248 /* Set up the common data portion
3249 */
3250 pData->Header.PageVersion = pReq->Header.PageVersion;
3251 pData->Header.PageLength = pReq->Header.PageLength;
3252 pData->Header.PageNumber = pReq->Header.PageNumber;
3253 pData->Header.PageType = pReq->Header.PageType;
3254 pData->RequestedParameters = cpu_to_le32(requested);
3255 pData->Reserved = 0;
3256 pData->Configuration = cpu_to_le32(configuration);
3257
3258 dprintk((MYIOC_s_INFO_FMT
3259 "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n",
3260 ioc->name, id, (id | (bus<<8)),
3261 requested, configuration));
3262
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003263 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003264 }
3265
3266 return 0;
3267}
3268
3269/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3270/* mptscsih_writeIOCPage4 - write IOC Page 4
3271 * @hd: Pointer to a SCSI Host Structure
3272 * @target_id: write IOC Page4 for this ID & Bus
3273 *
3274 * Return: -EAGAIN if unable to obtain a Message Frame
3275 * or 0 if success.
3276 *
3277 * Remark: We do not wait for a return, write pages sequentially.
3278 */
3279static int
3280mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus)
3281{
3282 MPT_ADAPTER *ioc = hd->ioc;
3283 Config_t *pReq;
3284 IOCPage4_t *IOCPage4Ptr;
3285 MPT_FRAME_HDR *mf;
3286 dma_addr_t dataDma;
3287 u16 req_idx;
3288 u32 frameOffset;
3289 u32 flagsLength;
3290 int ii;
3291
3292 /* Get a MF for this command.
3293 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003294 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003295 dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003296 ioc->name));
3297 return -EAGAIN;
3298 }
3299
3300 /* Set the request and the data pointers.
3301 * Place data at end of MF.
3302 */
3303 pReq = (Config_t *)mf;
3304
3305 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3306 frameOffset = ioc->req_sz - sizeof(IOCPage4_t);
3307
3308 /* Complete the request frame (same for all requests).
3309 */
3310 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3311 pReq->Reserved = 0;
3312 pReq->ChainOffset = 0;
3313 pReq->Function = MPI_FUNCTION_CONFIG;
3314 pReq->ExtPageLength = 0;
3315 pReq->ExtPageType = 0;
3316 pReq->MsgFlags = 0;
3317 for (ii=0; ii < 8; ii++) {
3318 pReq->Reserved2[ii] = 0;
3319 }
3320
3321 IOCPage4Ptr = ioc->spi_data.pIocPg4;
3322 dataDma = ioc->spi_data.IocPg4_dma;
3323 ii = IOCPage4Ptr->ActiveSEP++;
3324 IOCPage4Ptr->SEP[ii].SEPTargetID = target_id;
3325 IOCPage4Ptr->SEP[ii].SEPBus = bus;
3326 pReq->Header = IOCPage4Ptr->Header;
3327 pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 ));
3328
3329 /* Add a SGE to the config request.
3330 */
3331 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
3332 (IOCPage4Ptr->Header.PageLength + ii) * 4;
3333
3334 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3335
3336 dinitprintk((MYIOC_s_INFO_FMT
3337 "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
3338 ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus));
3339
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003340 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003341
3342 return 0;
3343}
3344
3345/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3346/*
3347 * Bus Scan and Domain Validation functionality ...
3348 */
3349
3350/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3351/*
3352 * mptscsih_scandv_complete - Scan and DV callback routine registered
3353 * to Fustion MPT (base) driver.
3354 *
3355 * @ioc: Pointer to MPT_ADAPTER structure
3356 * @mf: Pointer to original MPT request frame
3357 * @mr: Pointer to MPT reply frame (NULL if TurboReply)
3358 *
3359 * This routine is called from mpt.c::mpt_interrupt() at the completion
3360 * of any SCSI IO request.
3361 * This routine is registered with the Fusion MPT (base) driver at driver
3362 * load/init time via the mpt_register() API call.
3363 *
3364 * Returns 1 indicating alloc'd request frame ptr should be freed.
3365 *
3366 * Remark: Sets a completion code and (possibly) saves sense data
3367 * in the IOC member localReply structure.
3368 * Used ONLY for DV and other internal commands.
3369 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003370int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003371mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
3372{
3373 MPT_SCSI_HOST *hd;
3374 SCSIIORequest_t *pReq;
3375 int completionCode;
3376 u16 req_idx;
3377
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003378 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
3379
Linus Torvalds1da177e2005-04-16 15:20:36 -07003380 if ((mf == NULL) ||
3381 (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
3382 printk(MYIOC_s_ERR_FMT
3383 "ScanDvComplete, %s req frame ptr! (=%p)\n",
3384 ioc->name, mf?"BAD":"NULL", (void *) mf);
3385 goto wakeup;
3386 }
3387
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 del_timer(&hd->timer);
3389 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3390 hd->ScsiLookup[req_idx] = NULL;
3391 pReq = (SCSIIORequest_t *) mf;
3392
3393 if (mf != hd->cmdPtr) {
3394 printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
3395 hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
3396 }
3397 hd->cmdPtr = NULL;
3398
3399 ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
3400 hd->ioc->name, mf, mr, req_idx));
3401
3402 hd->pLocal = &hd->localReply;
3403 hd->pLocal->scsiStatus = 0;
3404
3405 /* If target struct exists, clear sense valid flag.
3406 */
3407 if (mr == NULL) {
3408 completionCode = MPT_SCANDV_GOOD;
3409 } else {
3410 SCSIIOReply_t *pReply;
3411 u16 status;
3412 u8 scsi_status;
3413
3414 pReply = (SCSIIOReply_t *) mr;
3415
3416 status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
3417 scsi_status = pReply->SCSIStatus;
3418
3419 ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
3420 status, pReply->SCSIState, scsi_status,
3421 le32_to_cpu(pReply->IOCLogInfo)));
3422
3423 switch(status) {
3424
3425 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
3426 completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
3427 break;
3428
3429 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
3430 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
3431 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
3432 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
3433 completionCode = MPT_SCANDV_DID_RESET;
3434 break;
3435
3436 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
3437 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
3438 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
3439 if (pReply->Function == MPI_FUNCTION_CONFIG) {
3440 ConfigReply_t *pr = (ConfigReply_t *)mr;
3441 completionCode = MPT_SCANDV_GOOD;
3442 hd->pLocal->header.PageVersion = pr->Header.PageVersion;
3443 hd->pLocal->header.PageLength = pr->Header.PageLength;
3444 hd->pLocal->header.PageNumber = pr->Header.PageNumber;
3445 hd->pLocal->header.PageType = pr->Header.PageType;
3446
3447 } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
3448 /* If the RAID Volume request is successful,
3449 * return GOOD, else indicate that
3450 * some type of error occurred.
3451 */
3452 MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
Christoph Hellwig637fa992005-08-18 16:25:44 +02003453 if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003454 completionCode = MPT_SCANDV_GOOD;
3455 else
3456 completionCode = MPT_SCANDV_SOME_ERROR;
3457
3458 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
3459 u8 *sense_data;
3460 int sz;
3461
3462 /* save sense data in global structure
3463 */
3464 completionCode = MPT_SCANDV_SENSE;
3465 hd->pLocal->scsiStatus = scsi_status;
3466 sense_data = ((u8 *)hd->ioc->sense_buf_pool +
3467 (req_idx * MPT_SENSE_BUFFER_ALLOC));
3468
3469 sz = min_t(int, pReq->SenseBufferLength,
3470 SCSI_STD_SENSE_BYTES);
3471 memcpy(hd->pLocal->sense, sense_data, sz);
3472
3473 ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
3474 sense_data));
3475 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
3476 if (pReq->CDB[0] == INQUIRY)
3477 completionCode = MPT_SCANDV_ISSUE_SENSE;
3478 else
3479 completionCode = MPT_SCANDV_DID_RESET;
3480 }
3481 else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
3482 completionCode = MPT_SCANDV_DID_RESET;
3483 else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3484 completionCode = MPT_SCANDV_DID_RESET;
3485 else {
3486 completionCode = MPT_SCANDV_GOOD;
3487 hd->pLocal->scsiStatus = scsi_status;
3488 }
3489 break;
3490
3491 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
3492 if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3493 completionCode = MPT_SCANDV_DID_RESET;
3494 else
3495 completionCode = MPT_SCANDV_SOME_ERROR;
3496 break;
3497
3498 default:
3499 completionCode = MPT_SCANDV_SOME_ERROR;
3500 break;
3501
3502 } /* switch(status) */
3503
3504 ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n",
3505 completionCode));
3506 } /* end of address reply case */
3507
3508 hd->pLocal->completion = completionCode;
3509
3510 /* MF and RF are freed in mpt_interrupt
3511 */
3512wakeup:
3513 /* Free Chain buffers (will never chain) in scan or dv */
3514 //mptscsih_freeChainBuffers(ioc, req_idx);
3515
3516 /*
3517 * Wake up the original calling thread
3518 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003519 hd->scandv_wait_done = 1;
3520 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003521
3522 return 1;
3523}
3524
3525/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3526/* mptscsih_timer_expired - Call back for timer process.
3527 * Used only for dv functionality.
3528 * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
3529 *
3530 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003531void
3532mptscsih_timer_expired(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533{
3534 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
3535
3536 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
3537
3538 if (hd->cmdPtr) {
3539 MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
3540
3541 if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
3542 /* Desire to issue a task management request here.
3543 * TM requests MUST be single threaded.
3544 * If old eh code and no TM current, issue request.
3545 * If new eh code, do nothing. Wait for OS cmd timeout
3546 * for bus reset.
3547 */
3548 ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
3549 } else {
3550 /* Perform a FW reload */
3551 if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
3552 printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
3553 }
3554 }
3555 } else {
3556 /* This should NEVER happen */
3557 printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
3558 }
3559
3560 /* No more processing.
3561 * TM call will generate an interrupt for SCSI TM Management.
3562 * The FW will reply to all outstanding commands, callback will finish cleanup.
3563 * Hard reset clean-up will free all resources.
3564 */
3565 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
3566
3567 return;
3568}
3569
3570#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3571/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3572/* mptscsih_do_raid - Format and Issue a RAID volume request message.
3573 * @hd: Pointer to scsi host structure
3574 * @action: What do be done.
3575 * @id: Logical target id.
3576 * @bus: Target locations bus.
3577 *
3578 * Returns: < 0 on a fatal error
3579 * 0 on success
3580 *
3581 * Remark: Wait to return until reply processed by the ISR.
3582 */
3583static int
3584mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io)
3585{
3586 MpiRaidActionRequest_t *pReq;
3587 MPT_FRAME_HDR *mf;
3588 int in_isr;
3589
3590 in_isr = in_interrupt();
3591 if (in_isr) {
3592 dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n",
3593 hd->ioc->name));
3594 return -EPERM;
3595 }
3596
3597 /* Get and Populate a free Frame
3598 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003599 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600 ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
3601 hd->ioc->name));
3602 return -EAGAIN;
3603 }
3604 pReq = (MpiRaidActionRequest_t *)mf;
3605 pReq->Action = action;
3606 pReq->Reserved1 = 0;
3607 pReq->ChainOffset = 0;
3608 pReq->Function = MPI_FUNCTION_RAID_ACTION;
3609 pReq->VolumeID = io->id;
3610 pReq->VolumeBus = io->bus;
3611 pReq->PhysDiskNum = io->physDiskNum;
3612 pReq->MsgFlags = 0;
3613 pReq->Reserved2 = 0;
3614 pReq->ActionDataWord = 0; /* Reserved for this action */
3615 //pReq->ActionDataSGE = 0;
3616
3617 mpt_add_sge((char *)&pReq->ActionDataSGE,
3618 MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
3619
3620 ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n",
3621 hd->ioc->name, action, io->id));
3622
3623 hd->pLocal = NULL;
3624 hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003625 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626
3627 /* Save cmd pointer, for resource free if timeout or
3628 * FW reload occurs
3629 */
3630 hd->cmdPtr = mf;
3631
3632 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003633 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3634 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003635
3636 if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD))
3637 return -1;
3638
3639 return 0;
3640}
3641#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
3642
3643/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3644/**
3645 * mptscsih_do_cmd - Do internal command.
3646 * @hd: MPT_SCSI_HOST pointer
3647 * @io: INTERNAL_CMD pointer.
3648 *
3649 * Issue the specified internally generated command and do command
3650 * specific cleanup. For bus scan / DV only.
3651 * NOTES: If command is Inquiry and status is good,
3652 * initialize a target structure, save the data
3653 *
3654 * Remark: Single threaded access only.
3655 *
3656 * Return:
3657 * < 0 if an illegal command or no resources
3658 *
3659 * 0 if good
3660 *
3661 * > 0 if command complete but some type of completion error.
3662 */
3663static int
3664mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
3665{
3666 MPT_FRAME_HDR *mf;
3667 SCSIIORequest_t *pScsiReq;
3668 SCSIIORequest_t ReqCopy;
3669 int my_idx, ii, dir;
3670 int rc, cmdTimeout;
3671 int in_isr;
3672 char cmdLen;
3673 char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3674 char cmd = io->cmd;
3675
3676 in_isr = in_interrupt();
3677 if (in_isr) {
3678 dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
3679 hd->ioc->name));
3680 return -EPERM;
3681 }
3682
3683
3684 /* Set command specific information
3685 */
3686 switch (cmd) {
3687 case INQUIRY:
3688 cmdLen = 6;
3689 dir = MPI_SCSIIO_CONTROL_READ;
3690 CDB[0] = cmd;
3691 CDB[4] = io->size;
3692 cmdTimeout = 10;
3693 break;
3694
3695 case TEST_UNIT_READY:
3696 cmdLen = 6;
3697 dir = MPI_SCSIIO_CONTROL_READ;
3698 cmdTimeout = 10;
3699 break;
3700
3701 case START_STOP:
3702 cmdLen = 6;
3703 dir = MPI_SCSIIO_CONTROL_READ;
3704 CDB[0] = cmd;
3705 CDB[4] = 1; /*Spin up the disk */
3706 cmdTimeout = 15;
3707 break;
3708
3709 case REQUEST_SENSE:
3710 cmdLen = 6;
3711 CDB[0] = cmd;
3712 CDB[4] = io->size;
3713 dir = MPI_SCSIIO_CONTROL_READ;
3714 cmdTimeout = 10;
3715 break;
3716
3717 case READ_BUFFER:
3718 cmdLen = 10;
3719 dir = MPI_SCSIIO_CONTROL_READ;
3720 CDB[0] = cmd;
3721 if (io->flags & MPT_ICFLAG_ECHO) {
3722 CDB[1] = 0x0A;
3723 } else {
3724 CDB[1] = 0x02;
3725 }
3726
3727 if (io->flags & MPT_ICFLAG_BUF_CAP) {
3728 CDB[1] |= 0x01;
3729 }
3730 CDB[6] = (io->size >> 16) & 0xFF;
3731 CDB[7] = (io->size >> 8) & 0xFF;
3732 CDB[8] = io->size & 0xFF;
3733 cmdTimeout = 10;
3734 break;
3735
3736 case WRITE_BUFFER:
3737 cmdLen = 10;
3738 dir = MPI_SCSIIO_CONTROL_WRITE;
3739 CDB[0] = cmd;
3740 if (io->flags & MPT_ICFLAG_ECHO) {
3741 CDB[1] = 0x0A;
3742 } else {
3743 CDB[1] = 0x02;
3744 }
3745 CDB[6] = (io->size >> 16) & 0xFF;
3746 CDB[7] = (io->size >> 8) & 0xFF;
3747 CDB[8] = io->size & 0xFF;
3748 cmdTimeout = 10;
3749 break;
3750
3751 case RESERVE:
3752 cmdLen = 6;
3753 dir = MPI_SCSIIO_CONTROL_READ;
3754 CDB[0] = cmd;
3755 cmdTimeout = 10;
3756 break;
3757
3758 case RELEASE:
3759 cmdLen = 6;
3760 dir = MPI_SCSIIO_CONTROL_READ;
3761 CDB[0] = cmd;
3762 cmdTimeout = 10;
3763 break;
3764
3765 case SYNCHRONIZE_CACHE:
3766 cmdLen = 10;
3767 dir = MPI_SCSIIO_CONTROL_READ;
3768 CDB[0] = cmd;
3769// CDB[1] = 0x02; /* set immediate bit */
3770 cmdTimeout = 10;
3771 break;
3772
3773 default:
3774 /* Error Case */
3775 return -EFAULT;
3776 }
3777
3778 /* Get and Populate a free Frame
3779 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003780 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003781 ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
3782 hd->ioc->name));
3783 return -EBUSY;
3784 }
3785
3786 pScsiReq = (SCSIIORequest_t *) mf;
3787
3788 /* Get the request index */
3789 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3790 ADD_INDEX_LOG(my_idx); /* for debug */
3791
3792 if (io->flags & MPT_ICFLAG_PHYS_DISK) {
3793 pScsiReq->TargetID = io->physDiskNum;
3794 pScsiReq->Bus = 0;
3795 pScsiReq->ChainOffset = 0;
3796 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
3797 } else {
3798 pScsiReq->TargetID = io->id;
3799 pScsiReq->Bus = io->bus;
3800 pScsiReq->ChainOffset = 0;
3801 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
3802 }
3803
3804 pScsiReq->CDBLength = cmdLen;
3805 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
3806
3807 pScsiReq->Reserved = 0;
3808
3809 pScsiReq->MsgFlags = mpt_msg_flags();
3810 /* MsgContext set in mpt_get_msg_fram call */
3811
3812 for (ii=0; ii < 8; ii++)
3813 pScsiReq->LUN[ii] = 0;
3814 pScsiReq->LUN[1] = io->lun;
3815
3816 if (io->flags & MPT_ICFLAG_TAGGED_CMD)
3817 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
3818 else
3819 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3820
3821 if (cmd == REQUEST_SENSE) {
3822 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3823 ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n",
3824 hd->ioc->name, cmd));
3825 }
3826
3827 for (ii=0; ii < 16; ii++)
3828 pScsiReq->CDB[ii] = CDB[ii];
3829
3830 pScsiReq->DataLength = cpu_to_le32(io->size);
3831 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
3832 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
3833
3834 ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
3835 hd->ioc->name, cmd, io->bus, io->id, io->lun));
3836
3837 if (dir == MPI_SCSIIO_CONTROL_READ) {
3838 mpt_add_sge((char *) &pScsiReq->SGL,
3839 MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
3840 io->data_dma);
3841 } else {
3842 mpt_add_sge((char *) &pScsiReq->SGL,
3843 MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
3844 io->data_dma);
3845 }
3846
3847 /* The ISR will free the request frame, but we need
3848 * the information to initialize the target. Duplicate.
3849 */
3850 memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
3851
3852 /* Issue this command after:
3853 * finish init
3854 * add timer
3855 * Wait until the reply has been received
3856 * ScsiScanDvCtx callback function will
3857 * set hd->pLocal;
3858 * set scandv_wait_done and call wake_up
3859 */
3860 hd->pLocal = NULL;
3861 hd->timer.expires = jiffies + HZ*cmdTimeout;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003862 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003863
3864 /* Save cmd pointer, for resource free if timeout or
3865 * FW reload occurs
3866 */
3867 hd->cmdPtr = mf;
3868
3869 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003870 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3871 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003872
3873 if (hd->pLocal) {
3874 rc = hd->pLocal->completion;
3875 hd->pLocal->skip = 0;
3876
3877 /* Always set fatal error codes in some cases.
3878 */
3879 if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
3880 rc = -ENXIO;
3881 else if (rc == MPT_SCANDV_SOME_ERROR)
3882 rc = -rc;
3883 } else {
3884 rc = -EFAULT;
3885 /* This should never happen. */
3886 ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
3887 hd->ioc->name));
3888 }
3889
3890 return rc;
3891}
3892
3893/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3894/**
3895 * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
3896 * @hd: Pointer to MPT_SCSI_HOST structure
3897 * @portnum: IOC port number
3898 *
3899 * Uses the ISR, but with special processing.
3900 * MUST be single-threaded.
3901 *
3902 * Return: 0 on completion
3903 */
3904static int
3905mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum)
3906{
3907 MPT_ADAPTER *ioc= hd->ioc;
3908 VirtDevice *pTarget;
3909 SCSIDevicePage1_t *pcfg1Data = NULL;
3910 INTERNAL_CMD iocmd;
3911 CONFIGPARMS cfg;
3912 dma_addr_t cfg1_dma_addr = -1;
3913 ConfigPageHeader_t header1;
3914 int bus = 0;
3915 int id = 0;
3916 int lun;
3917 int indexed_lun, lun_index;
3918 int hostId = ioc->pfacts[portnum].PortSCSIID;
3919 int max_id;
3920 int requested, configuration, data;
3921 int doConfig = 0;
3922 u8 flags, factor;
3923
3924 max_id = ioc->sh->max_id - 1;
3925
3926 /* Following parameters will not change
3927 * in this routine.
3928 */
3929 iocmd.cmd = SYNCHRONIZE_CACHE;
3930 iocmd.flags = 0;
3931 iocmd.physDiskNum = -1;
3932 iocmd.data = NULL;
3933 iocmd.data_dma = -1;
3934 iocmd.size = 0;
3935 iocmd.rsvd = iocmd.rsvd2 = 0;
3936
3937 /* No SCSI hosts
3938 */
3939 if (hd->Targets == NULL)
3940 return 0;
3941
3942 /* Skip the host
3943 */
3944 if (id == hostId)
3945 id++;
3946
3947 /* Write SDP1 for all SCSI devices
3948 * Alloc memory and set up config buffer
3949 */
3950 if (ioc->bus_type == SCSI) {
3951 if (ioc->spi_data.sdp1length > 0) {
3952 pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev,
3953 ioc->spi_data.sdp1length * 4, &cfg1_dma_addr);
3954
3955 if (pcfg1Data != NULL) {
3956 doConfig = 1;
3957 header1.PageVersion = ioc->spi_data.sdp1version;
3958 header1.PageLength = ioc->spi_data.sdp1length;
3959 header1.PageNumber = 1;
3960 header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
Christoph Hellwig69218ee2005-08-18 16:26:15 +02003961 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962 cfg.physAddr = cfg1_dma_addr;
3963 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3964 cfg.dir = 1;
3965 cfg.timeout = 0;
3966 }
3967 }
3968 }
3969
3970 /* loop through all devices on this port
3971 */
3972 while (bus < MPT_MAX_BUS) {
3973 iocmd.bus = bus;
3974 iocmd.id = id;
3975 pTarget = hd->Targets[(int)id];
3976
3977 if (doConfig) {
3978
3979 /* Set the negotiation flags */
3980 if (pTarget && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) {
3981 flags = pTarget->negoFlags;
3982 } else {
3983 flags = hd->ioc->spi_data.noQas;
3984 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3985 data = hd->ioc->spi_data.nvram[id];
3986
3987 if (data & MPT_NVRAM_WIDE_DISABLE)
3988 flags |= MPT_TARGET_NO_NEGO_WIDE;
3989
3990 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
3991 if ((factor == 0) || (factor == MPT_ASYNC))
3992 flags |= MPT_TARGET_NO_NEGO_SYNC;
3993 }
3994 }
3995
3996 /* Force to async, narrow */
3997 mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
3998 &configuration, flags);
3999 dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC "
4000 "offset=0 negoFlags=%x request=%x config=%x\n",
4001 id, flags, requested, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02004002 pcfg1Data->RequestedParameters = cpu_to_le32(requested);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 pcfg1Data->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02004004 pcfg1Data->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 cfg.pageAddr = (bus<<8) | id;
4006 mpt_config(hd->ioc, &cfg);
4007 }
4008
4009 /* If target Ptr NULL or if this target is NOT a disk, skip.
4010 */
4011 if ((pTarget) && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)){
4012 for (lun=0; lun <= MPT_LAST_LUN; lun++) {
4013 /* If LUN present, issue the command
4014 */
4015 lun_index = (lun >> 5); /* 32 luns per lun_index */
4016 indexed_lun = (lun % 32);
4017 if (pTarget->luns[lun_index] & (1<<indexed_lun)) {
4018 iocmd.lun = lun;
4019 (void) mptscsih_do_cmd(hd, &iocmd);
4020 }
4021 }
4022 }
4023
4024 /* get next relevant device */
4025 id++;
4026
4027 if (id == hostId)
4028 id++;
4029
4030 if (id > max_id) {
4031 id = 0;
4032 bus++;
4033 }
4034 }
4035
4036 if (pcfg1Data) {
4037 pci_free_consistent(ioc->pcidev, header1.PageLength * 4, pcfg1Data, cfg1_dma_addr);
4038 }
4039
4040 return 0;
4041}
4042
4043#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
4044/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4045/**
4046 * mptscsih_domainValidation - Top level handler for domain validation.
4047 * @hd: Pointer to MPT_SCSI_HOST structure.
4048 *
4049 * Uses the ISR, but with special processing.
4050 * Called from schedule, should not be in interrupt mode.
4051 * While thread alive, do dv for all devices needing dv
4052 *
4053 * Return: None.
4054 */
4055static void
4056mptscsih_domainValidation(void *arg)
4057{
4058 MPT_SCSI_HOST *hd;
4059 MPT_ADAPTER *ioc;
4060 unsigned long flags;
4061 int id, maxid, dvStatus, did;
4062 int ii, isPhysDisk;
4063
4064 spin_lock_irqsave(&dvtaskQ_lock, flags);
4065 dvtaskQ_active = 1;
4066 if (dvtaskQ_release) {
4067 dvtaskQ_active = 0;
4068 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4069 return;
4070 }
4071 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4072
4073 /* For this ioc, loop through all devices and do dv to each device.
4074 * When complete with this ioc, search through the ioc list, and
4075 * for each scsi ioc found, do dv for all devices. Exit when no
4076 * device needs dv.
4077 */
4078 did = 1;
4079 while (did) {
4080 did = 0;
4081 list_for_each_entry(ioc, &ioc_list, list) {
4082 spin_lock_irqsave(&dvtaskQ_lock, flags);
4083 if (dvtaskQ_release) {
4084 dvtaskQ_active = 0;
4085 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4086 return;
4087 }
4088 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4089
4090 msleep(250);
4091
4092 /* DV only to SCSI adapters */
4093 if (ioc->bus_type != SCSI)
4094 continue;
4095
4096 /* Make sure everything looks ok */
4097 if (ioc->sh == NULL)
4098 continue;
4099
4100 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
4101 if (hd == NULL)
4102 continue;
4103
4104 if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) {
4105 mpt_read_ioc_pg_3(ioc);
4106 if (ioc->spi_data.pIocPg3) {
4107 Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
4108 int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
4109
4110 while (numPDisk) {
4111 if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE)
4112 ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
4113
4114 pPDisk++;
4115 numPDisk--;
4116 }
4117 }
4118 ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3;
4119 }
4120
4121 maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
4122
4123 for (id = 0; id < maxid; id++) {
4124 spin_lock_irqsave(&dvtaskQ_lock, flags);
4125 if (dvtaskQ_release) {
4126 dvtaskQ_active = 0;
4127 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4128 return;
4129 }
4130 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4131 dvStatus = hd->ioc->spi_data.dvStatus[id];
4132
4133 if (dvStatus & MPT_SCSICFG_NEED_DV) {
4134 did++;
4135 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING;
4136 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV;
4137
4138 msleep(250);
4139
4140 /* If hidden phys disk, block IO's to all
4141 * raid volumes
4142 * else, process normally
4143 */
4144 isPhysDisk = mptscsih_is_phys_disk(ioc, id);
4145 if (isPhysDisk) {
4146 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4147 if (hd->ioc->spi_data.isRaid & (1 << ii)) {
4148 hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING;
4149 }
4150 }
4151 }
4152
4153 if (mptscsih_doDv(hd, 0, id) == 1) {
4154 /* Untagged device was busy, try again
4155 */
4156 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV;
4157 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING;
4158 } else {
4159 /* DV is complete. Clear flags.
4160 */
4161 hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING);
4162 }
4163
4164 if (isPhysDisk) {
4165 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4166 if (hd->ioc->spi_data.isRaid & (1 << ii)) {
4167 hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING;
4168 }
4169 }
4170 }
4171
4172 if (hd->ioc->spi_data.noQas)
4173 mptscsih_qas_check(hd, id);
4174 }
4175 }
4176 }
4177 }
4178
4179 spin_lock_irqsave(&dvtaskQ_lock, flags);
4180 dvtaskQ_active = 0;
4181 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4182
4183 return;
4184}
4185
4186/* Search IOC page 3 to determine if this is hidden physical disk
4187 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004188static int
4189mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004190{
4191 if (ioc->spi_data.pIocPg3) {
4192 Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
4193 int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
4194
4195 while (numPDisk) {
4196 if (pPDisk->PhysDiskID == id) {
4197 return 1;
4198 }
4199 pPDisk++;
4200 numPDisk--;
4201 }
4202 }
4203 return 0;
4204}
4205
4206/* Write SDP1 if no QAS has been enabled
4207 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004208static void
4209mptscsih_qas_check(MPT_SCSI_HOST *hd, int id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004210{
4211 VirtDevice *pTarget;
4212 int ii;
4213
4214 if (hd->Targets == NULL)
4215 return;
4216
4217 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4218 if (ii == id)
4219 continue;
4220
4221 if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0)
4222 continue;
4223
4224 pTarget = hd->Targets[ii];
4225
4226 if ((pTarget != NULL) && (!pTarget->raidVolume)) {
4227 if ((pTarget->negoFlags & hd->ioc->spi_data.noQas) == 0) {
4228 pTarget->negoFlags |= hd->ioc->spi_data.noQas;
4229 dnegoprintk(("writeSDP1: id=%d flags=0\n", id));
4230 mptscsih_writeSDP1(hd, 0, ii, 0);
4231 }
4232 } else {
4233 if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) {
4234 dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id));
4235 mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM);
4236 }
4237 }
4238 }
4239 return;
4240}
4241
4242
4243
4244#define MPT_GET_NVRAM_VALS 0x01
4245#define MPT_UPDATE_MAX 0x02
4246#define MPT_SET_MAX 0x04
4247#define MPT_SET_MIN 0x08
4248#define MPT_FALLBACK 0x10
4249#define MPT_SAVE 0x20
4250
4251/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4252/**
4253 * mptscsih_doDv - Perform domain validation to a target.
4254 * @hd: Pointer to MPT_SCSI_HOST structure.
4255 * @portnum: IOC port number.
4256 * @target: Physical ID of this target
4257 *
4258 * Uses the ISR, but with special processing.
4259 * MUST be single-threaded.
4260 * Test will exit if target is at async & narrow.
4261 *
4262 * Return: None.
4263 */
4264static int
4265mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id)
4266{
4267 MPT_ADAPTER *ioc = hd->ioc;
4268 VirtDevice *pTarget;
4269 SCSIDevicePage1_t *pcfg1Data;
4270 SCSIDevicePage0_t *pcfg0Data;
4271 u8 *pbuf1;
4272 u8 *pbuf2;
4273 u8 *pDvBuf;
4274 dma_addr_t dvbuf_dma = -1;
4275 dma_addr_t buf1_dma = -1;
4276 dma_addr_t buf2_dma = -1;
4277 dma_addr_t cfg1_dma_addr = -1;
4278 dma_addr_t cfg0_dma_addr = -1;
4279 ConfigPageHeader_t header1;
4280 ConfigPageHeader_t header0;
4281 DVPARAMETERS dv;
4282 INTERNAL_CMD iocmd;
4283 CONFIGPARMS cfg;
4284 int dv_alloc = 0;
4285 int rc, sz = 0;
4286 int bufsize = 0;
4287 int dataBufSize = 0;
4288 int echoBufSize = 0;
4289 int notDone;
4290 int patt;
4291 int repeat;
4292 int retcode = 0;
4293 int nfactor = MPT_ULTRA320;
4294 char firstPass = 1;
4295 char doFallback = 0;
4296 char readPage0;
4297 char bus, lun;
4298 char inq0 = 0;
4299
4300 if (ioc->spi_data.sdp1length == 0)
4301 return 0;
4302
4303 if (ioc->spi_data.sdp0length == 0)
4304 return 0;
4305
4306 /* If multiple buses are used, require that the initiator
4307 * id be the same on all buses.
4308 */
4309 if (id == ioc->pfacts[0].PortSCSIID)
4310 return 0;
4311
4312 lun = 0;
4313 bus = (u8) bus_number;
4314 ddvtprintk((MYIOC_s_NOTE_FMT
4315 "DV started: bus=%d, id=%d dv @ %p\n",
4316 ioc->name, bus, id, &dv));
4317
4318 /* Prep DV structure
4319 */
4320 memset (&dv, 0, sizeof(DVPARAMETERS));
4321 dv.id = id;
4322
4323 /* Populate tmax with the current maximum
4324 * transfer parameters for this target.
4325 * Exit if narrow and async.
4326 */
4327 dv.cmd = MPT_GET_NVRAM_VALS;
4328 mptscsih_dv_parms(hd, &dv, NULL);
4329
4330 /* Prep SCSI IO structure
4331 */
4332 iocmd.id = id;
4333 iocmd.bus = bus;
4334 iocmd.lun = lun;
4335 iocmd.flags = 0;
4336 iocmd.physDiskNum = -1;
4337 iocmd.rsvd = iocmd.rsvd2 = 0;
4338
4339 pTarget = hd->Targets[id];
4340
4341 /* Use tagged commands if possible.
4342 */
4343 if (pTarget) {
4344 if (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
4345 iocmd.flags |= MPT_ICFLAG_TAGGED_CMD;
4346 else {
4347 if (hd->ioc->facts.FWVersion.Word < 0x01000600)
4348 return 0;
4349
4350 if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4351 (hd->ioc->facts.FWVersion.Word < 0x01010B00))
4352 return 0;
4353 }
4354 }
4355
4356 /* Prep cfg structure
4357 */
4358 cfg.pageAddr = (bus<<8) | id;
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004359 cfg.cfghdr.hdr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004360
4361 /* Prep SDP0 header
4362 */
4363 header0.PageVersion = ioc->spi_data.sdp0version;
4364 header0.PageLength = ioc->spi_data.sdp0length;
4365 header0.PageNumber = 0;
4366 header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4367
4368 /* Prep SDP1 header
4369 */
4370 header1.PageVersion = ioc->spi_data.sdp1version;
4371 header1.PageLength = ioc->spi_data.sdp1length;
4372 header1.PageNumber = 1;
4373 header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4374
4375 if (header0.PageLength & 1)
4376 dv_alloc = (header0.PageLength * 4) + 4;
4377
4378 dv_alloc += (2048 + (header1.PageLength * 4));
4379
4380 pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma);
4381 if (pDvBuf == NULL)
4382 return 0;
4383
4384 sz = 0;
4385 pbuf1 = (u8 *)pDvBuf;
4386 buf1_dma = dvbuf_dma;
4387 sz +=1024;
4388
4389 pbuf2 = (u8 *) (pDvBuf + sz);
4390 buf2_dma = dvbuf_dma + sz;
4391 sz +=1024;
4392
4393 pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz);
4394 cfg0_dma_addr = dvbuf_dma + sz;
4395 sz += header0.PageLength * 4;
4396
4397 /* 8-byte alignment
4398 */
4399 if (header0.PageLength & 1)
4400 sz += 4;
4401
4402 pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz);
4403 cfg1_dma_addr = dvbuf_dma + sz;
4404
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004405 /* Skip this ID? Set cfg.cfghdr.hdr to force config page write
Linus Torvalds1da177e2005-04-16 15:20:36 -07004406 */
4407 {
4408 ScsiCfgData *pspi_data = &hd->ioc->spi_data;
4409 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
4410 /* Set the factor from nvram */
4411 nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8;
4412 if (nfactor < pspi_data->minSyncFactor )
4413 nfactor = pspi_data->minSyncFactor;
4414
4415 if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE) ||
4416 (pspi_data->PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV) ) {
4417
4418 ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
4419 ioc->name, bus, id, lun));
4420
4421 dv.cmd = MPT_SET_MAX;
4422 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004423 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004424
4425 /* Save the final negotiated settings to
4426 * SCSI device page 1.
4427 */
4428 cfg.physAddr = cfg1_dma_addr;
4429 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4430 cfg.dir = 1;
4431 mpt_config(hd->ioc, &cfg);
4432 goto target_done;
4433 }
4434 }
4435 }
4436
4437 /* Finish iocmd inititialization - hidden or visible disk? */
4438 if (ioc->spi_data.pIocPg3) {
4439 /* Search IOC page 3 for matching id
4440 */
4441 Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
4442 int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
4443
4444 while (numPDisk) {
4445 if (pPDisk->PhysDiskID == id) {
4446 /* match */
4447 iocmd.flags |= MPT_ICFLAG_PHYS_DISK;
4448 iocmd.physDiskNum = pPDisk->PhysDiskNum;
4449
4450 /* Quiesce the IM
4451 */
4452 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) {
4453 ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
4454 goto target_done;
4455 }
4456 break;
4457 }
4458 pPDisk++;
4459 numPDisk--;
4460 }
4461 }
4462
4463 /* RAID Volume ID's may double for a physical device. If RAID but
4464 * not a physical ID as well, skip DV.
4465 */
4466 if ((hd->ioc->spi_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK))
4467 goto target_done;
4468
4469
4470 /* Basic Test.
4471 * Async & Narrow - Inquiry
4472 * Async & Narrow - Inquiry
4473 * Maximum transfer rate - Inquiry
4474 * Compare buffers:
4475 * If compare, test complete.
4476 * If miscompare and first pass, repeat
4477 * If miscompare and not first pass, fall back and repeat
4478 */
4479 hd->pLocal = NULL;
4480 readPage0 = 0;
4481 sz = SCSI_MAX_INQUIRY_BYTES;
4482 rc = MPT_SCANDV_GOOD;
4483 while (1) {
4484 ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id));
4485 retcode = 0;
4486 dv.cmd = MPT_SET_MIN;
4487 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4488
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004489 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004490 cfg.physAddr = cfg1_dma_addr;
4491 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4492 cfg.dir = 1;
4493 if (mpt_config(hd->ioc, &cfg) != 0)
4494 goto target_done;
4495
4496 /* Wide - narrow - wide workaround case
4497 */
4498 if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
4499 /* Send an untagged command to reset disk Qs corrupted
4500 * when a parity error occurs on a Request Sense.
4501 */
4502 if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
4503 ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4504 (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) {
4505
4506 iocmd.cmd = REQUEST_SENSE;
4507 iocmd.data_dma = buf1_dma;
4508 iocmd.data = pbuf1;
4509 iocmd.size = 0x12;
4510 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4511 goto target_done;
4512 else {
4513 if (hd->pLocal == NULL)
4514 goto target_done;
4515 rc = hd->pLocal->completion;
4516 if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) {
4517 dv.max.width = 0;
4518 doFallback = 0;
4519 } else
4520 goto target_done;
4521 }
4522 } else
4523 goto target_done;
4524 }
4525
4526 iocmd.cmd = INQUIRY;
4527 iocmd.data_dma = buf1_dma;
4528 iocmd.data = pbuf1;
4529 iocmd.size = sz;
4530 memset(pbuf1, 0x00, sz);
4531 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4532 goto target_done;
4533 else {
4534 if (hd->pLocal == NULL)
4535 goto target_done;
4536 rc = hd->pLocal->completion;
4537 if (rc == MPT_SCANDV_GOOD) {
4538 if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) {
4539 if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
4540 retcode = 1;
4541 else
4542 retcode = 0;
4543
4544 goto target_done;
4545 }
4546 } else if (rc == MPT_SCANDV_SENSE) {
4547 ;
4548 } else {
4549 /* If first command doesn't complete
4550 * with a good status or with a check condition,
4551 * exit.
4552 */
4553 goto target_done;
4554 }
4555 }
4556
4557 /* Reset the size for disks
4558 */
4559 inq0 = (*pbuf1) & 0x1F;
4560 if ((inq0 == 0) && pTarget && !pTarget->raidVolume) {
4561 sz = 0x40;
4562 iocmd.size = sz;
4563 }
4564
4565 /* Another GEM workaround. Check peripheral device type,
4566 * if PROCESSOR, quit DV.
4567 */
4568 if (inq0 == TYPE_PROCESSOR) {
4569 mptscsih_initTarget(hd,
4570 bus,
4571 id,
4572 lun,
4573 pbuf1,
4574 sz);
4575 goto target_done;
4576 }
4577
4578 if (inq0 > 0x08)
4579 goto target_done;
4580
4581 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4582 goto target_done;
4583
4584 if (sz == 0x40) {
4585 if ((pTarget->maxWidth == 1) && (pTarget->maxOffset) && (nfactor < 0x0A)
4586 && (pTarget->minSyncFactor > 0x09)) {
4587 if ((pbuf1[56] & 0x04) == 0)
4588 ;
4589 else if ((pbuf1[56] & 0x01) == 1) {
4590 pTarget->minSyncFactor =
4591 nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320;
4592 } else {
4593 pTarget->minSyncFactor =
4594 nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160;
4595 }
4596
4597 dv.max.factor = pTarget->minSyncFactor;
4598
4599 if ((pbuf1[56] & 0x02) == 0) {
4600 pTarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
4601 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004602 ddvprintk((MYIOC_s_NOTE_FMT
4603 "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004604 ioc->name, id, pbuf1[56]));
4605 }
4606 }
4607 }
4608
4609 if (doFallback)
4610 dv.cmd = MPT_FALLBACK;
4611 else
4612 dv.cmd = MPT_SET_MAX;
4613
4614 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4615 if (mpt_config(hd->ioc, &cfg) != 0)
4616 goto target_done;
4617
4618 if ((!dv.now.width) && (!dv.now.offset))
4619 goto target_done;
4620
4621 iocmd.cmd = INQUIRY;
4622 iocmd.data_dma = buf2_dma;
4623 iocmd.data = pbuf2;
4624 iocmd.size = sz;
4625 memset(pbuf2, 0x00, sz);
4626 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4627 goto target_done;
4628 else if (hd->pLocal == NULL)
4629 goto target_done;
4630 else {
4631 /* Save the return code.
4632 * If this is the first pass,
4633 * read SCSI Device Page 0
4634 * and update the target max parameters.
4635 */
4636 rc = hd->pLocal->completion;
4637 doFallback = 0;
4638 if (rc == MPT_SCANDV_GOOD) {
4639 if (!readPage0) {
4640 u32 sdp0_info;
4641 u32 sdp0_nego;
4642
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004643 cfg.cfghdr.hdr = &header0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004644 cfg.physAddr = cfg0_dma_addr;
4645 cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
4646 cfg.dir = 0;
4647
4648 if (mpt_config(hd->ioc, &cfg) != 0)
4649 goto target_done;
4650
4651 sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E;
4652 sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8;
4653
4654 /* Quantum and Fujitsu workarounds.
4655 * Quantum: PPR U320 -> PPR reply with Ultra2 and wide
4656 * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
4657 * Resetart with a request for U160.
4658 */
4659 if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
4660 doFallback = 1;
4661 } else {
4662 dv.cmd = MPT_UPDATE_MAX;
4663 mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data);
4664 /* Update the SCSI device page 1 area
4665 */
4666 pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters;
4667 readPage0 = 1;
4668 }
4669 }
4670
4671 /* Quantum workaround. Restart this test will the fallback
4672 * flag set.
4673 */
4674 if (doFallback == 0) {
4675 if (memcmp(pbuf1, pbuf2, sz) != 0) {
4676 if (!firstPass)
4677 doFallback = 1;
4678 } else {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004679 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680 "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id));
4681 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE;
4682 mptscsih_initTarget(hd,
4683 bus,
4684 id,
4685 lun,
4686 pbuf1,
4687 sz);
4688 break; /* test complete */
4689 }
4690 }
4691
4692
4693 } else if (rc == MPT_SCANDV_ISSUE_SENSE)
4694 doFallback = 1; /* set fallback flag */
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004695 else if ((rc == MPT_SCANDV_DID_RESET) ||
4696 (rc == MPT_SCANDV_SENSE) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004697 (rc == MPT_SCANDV_FALLBACK))
4698 doFallback = 1; /* set fallback flag */
4699 else
4700 goto target_done;
4701
4702 firstPass = 0;
4703 }
4704 }
4705 ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id));
4706
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004707 if (ioc->spi_data.mpt_dv == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004708 goto target_done;
4709
4710 inq0 = (*pbuf1) & 0x1F;
4711
4712 /* Continue only for disks
4713 */
4714 if (inq0 != 0)
4715 goto target_done;
4716
4717 if ( ioc->spi_data.PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY )
4718 goto target_done;
4719
4720 /* Start the Enhanced Test.
4721 * 0) issue TUR to clear out check conditions
4722 * 1) read capacity of echo (regular) buffer
4723 * 2) reserve device
4724 * 3) do write-read-compare data pattern test
4725 * 4) release
4726 * 5) update nego parms to target struct
4727 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004728 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004729 cfg.physAddr = cfg1_dma_addr;
4730 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4731 cfg.dir = 1;
4732
4733 iocmd.cmd = TEST_UNIT_READY;
4734 iocmd.data_dma = -1;
4735 iocmd.data = NULL;
4736 iocmd.size = 0;
4737 notDone = 1;
4738 while (notDone) {
4739 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4740 goto target_done;
4741
4742 if (hd->pLocal == NULL)
4743 goto target_done;
4744
4745 rc = hd->pLocal->completion;
4746 if (rc == MPT_SCANDV_GOOD)
4747 notDone = 0;
4748 else if (rc == MPT_SCANDV_SENSE) {
4749 u8 skey = hd->pLocal->sense[2] & 0x0F;
4750 u8 asc = hd->pLocal->sense[12];
4751 u8 ascq = hd->pLocal->sense[13];
4752 ddvprintk((MYIOC_s_INFO_FMT
4753 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4754 ioc->name, skey, asc, ascq));
4755
4756 if (skey == UNIT_ATTENTION)
4757 notDone++; /* repeat */
4758 else if ((skey == NOT_READY) &&
4759 (asc == 0x04)&&(ascq == 0x01)) {
4760 /* wait then repeat */
4761 mdelay (2000);
4762 notDone++;
4763 } else if ((skey == NOT_READY) && (asc == 0x3A)) {
4764 /* no medium, try read test anyway */
4765 notDone = 0;
4766 } else {
4767 /* All other errors are fatal.
4768 */
4769 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4770 ioc->name));
4771 goto target_done;
4772 }
4773 } else
4774 goto target_done;
4775 }
4776
4777 iocmd.cmd = READ_BUFFER;
4778 iocmd.data_dma = buf1_dma;
4779 iocmd.data = pbuf1;
4780 iocmd.size = 4;
4781 iocmd.flags |= MPT_ICFLAG_BUF_CAP;
4782
4783 dataBufSize = 0;
4784 echoBufSize = 0;
4785 for (patt = 0; patt < 2; patt++) {
4786 if (patt == 0)
4787 iocmd.flags |= MPT_ICFLAG_ECHO;
4788 else
4789 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4790
4791 notDone = 1;
4792 while (notDone) {
4793 bufsize = 0;
4794
4795 /* If not ready after 8 trials,
4796 * give up on this device.
4797 */
4798 if (notDone > 8)
4799 goto target_done;
4800
4801 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4802 goto target_done;
4803 else if (hd->pLocal == NULL)
4804 goto target_done;
4805 else {
4806 rc = hd->pLocal->completion;
4807 ddvprintk(("ReadBuffer Comp Code %d", rc));
4808 ddvprintk((" buff: %0x %0x %0x %0x\n",
4809 pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3]));
4810
4811 if (rc == MPT_SCANDV_GOOD) {
4812 notDone = 0;
4813 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4814 bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3];
4815 } else {
4816 bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3];
4817 }
4818 } else if (rc == MPT_SCANDV_SENSE) {
4819 u8 skey = hd->pLocal->sense[2] & 0x0F;
4820 u8 asc = hd->pLocal->sense[12];
4821 u8 ascq = hd->pLocal->sense[13];
4822 ddvprintk((MYIOC_s_INFO_FMT
4823 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4824 ioc->name, skey, asc, ascq));
4825 if (skey == ILLEGAL_REQUEST) {
4826 notDone = 0;
4827 } else if (skey == UNIT_ATTENTION) {
4828 notDone++; /* repeat */
4829 } else if ((skey == NOT_READY) &&
4830 (asc == 0x04)&&(ascq == 0x01)) {
4831 /* wait then repeat */
4832 mdelay (2000);
4833 notDone++;
4834 } else {
4835 /* All other errors are fatal.
4836 */
4837 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4838 ioc->name));
4839 goto target_done;
4840 }
4841 } else {
4842 /* All other errors are fatal
4843 */
4844 goto target_done;
4845 }
4846 }
4847 }
4848
4849 if (iocmd.flags & MPT_ICFLAG_ECHO)
4850 echoBufSize = bufsize;
4851 else
4852 dataBufSize = bufsize;
4853 }
4854 sz = 0;
4855 iocmd.flags &= ~MPT_ICFLAG_BUF_CAP;
4856
4857 /* Use echo buffers if possible,
4858 * Exit if both buffers are 0.
4859 */
4860 if (echoBufSize > 0) {
4861 iocmd.flags |= MPT_ICFLAG_ECHO;
4862 if (dataBufSize > 0)
4863 bufsize = min(echoBufSize, dataBufSize);
4864 else
4865 bufsize = echoBufSize;
4866 } else if (dataBufSize == 0)
4867 goto target_done;
4868
4869 ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name,
4870 (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize));
4871
4872 /* Data buffers for write-read-compare test max 1K.
4873 */
4874 sz = min(bufsize, 1024);
4875
4876 /* --- loop ----
4877 * On first pass, always issue a reserve.
4878 * On additional loops, only if a reset has occurred.
4879 * iocmd.flags indicates if echo or regular buffer
4880 */
4881 for (patt = 0; patt < 4; patt++) {
4882 ddvprintk(("Pattern %d\n", patt));
4883 if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) {
4884 iocmd.cmd = TEST_UNIT_READY;
4885 iocmd.data_dma = -1;
4886 iocmd.data = NULL;
4887 iocmd.size = 0;
4888 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4889 goto target_done;
4890
4891 iocmd.cmd = RELEASE;
4892 iocmd.data_dma = -1;
4893 iocmd.data = NULL;
4894 iocmd.size = 0;
4895 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4896 goto target_done;
4897 else if (hd->pLocal == NULL)
4898 goto target_done;
4899 else {
4900 rc = hd->pLocal->completion;
4901 ddvprintk(("Release rc %d\n", rc));
4902 if (rc == MPT_SCANDV_GOOD)
4903 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4904 else
4905 goto target_done;
4906 }
4907 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4908 }
4909 iocmd.flags &= ~MPT_ICFLAG_DID_RESET;
4910
4911 repeat = 5;
4912 while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) {
4913 iocmd.cmd = RESERVE;
4914 iocmd.data_dma = -1;
4915 iocmd.data = NULL;
4916 iocmd.size = 0;
4917 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4918 goto target_done;
4919 else if (hd->pLocal == NULL)
4920 goto target_done;
4921 else {
4922 rc = hd->pLocal->completion;
4923 if (rc == MPT_SCANDV_GOOD) {
4924 iocmd.flags |= MPT_ICFLAG_RESERVED;
4925 } else if (rc == MPT_SCANDV_SENSE) {
4926 /* Wait if coming ready
4927 */
4928 u8 skey = hd->pLocal->sense[2] & 0x0F;
4929 u8 asc = hd->pLocal->sense[12];
4930 u8 ascq = hd->pLocal->sense[13];
4931 ddvprintk((MYIOC_s_INFO_FMT
4932 "DV: Reserve Failed: ", ioc->name));
4933 ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4934 skey, asc, ascq));
4935
4936 if ((skey == NOT_READY) && (asc == 0x04)&&
4937 (ascq == 0x01)) {
4938 /* wait then repeat */
4939 mdelay (2000);
4940 notDone++;
4941 } else {
4942 ddvprintk((MYIOC_s_INFO_FMT
4943 "DV: Reserved Failed.", ioc->name));
4944 goto target_done;
4945 }
4946 } else {
4947 ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.",
4948 ioc->name));
4949 goto target_done;
4950 }
4951 }
4952 }
4953
4954 mptscsih_fillbuf(pbuf1, sz, patt, 1);
4955 iocmd.cmd = WRITE_BUFFER;
4956 iocmd.data_dma = buf1_dma;
4957 iocmd.data = pbuf1;
4958 iocmd.size = sz;
4959 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4960 goto target_done;
4961 else if (hd->pLocal == NULL)
4962 goto target_done;
4963 else {
4964 rc = hd->pLocal->completion;
4965 if (rc == MPT_SCANDV_GOOD)
4966 ; /* Issue read buffer */
4967 else if (rc == MPT_SCANDV_DID_RESET) {
4968 /* If using echo buffers, reset to data buffers.
4969 * Else do Fallback and restart
4970 * this test (re-issue reserve
4971 * because of bus reset).
4972 */
4973 if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) {
4974 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4975 } else {
4976 dv.cmd = MPT_FALLBACK;
4977 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4978
4979 if (mpt_config(hd->ioc, &cfg) != 0)
4980 goto target_done;
4981
4982 if ((!dv.now.width) && (!dv.now.offset))
4983 goto target_done;
4984 }
4985
4986 iocmd.flags |= MPT_ICFLAG_DID_RESET;
4987 patt = -1;
4988 continue;
4989 } else if (rc == MPT_SCANDV_SENSE) {
4990 /* Restart data test if UA, else quit.
4991 */
4992 u8 skey = hd->pLocal->sense[2] & 0x0F;
4993 ddvprintk((MYIOC_s_INFO_FMT
4994 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
4995 hd->pLocal->sense[12], hd->pLocal->sense[13]));
4996 if (skey == UNIT_ATTENTION) {
4997 patt = -1;
4998 continue;
4999 } else if (skey == ILLEGAL_REQUEST) {
5000 if (iocmd.flags & MPT_ICFLAG_ECHO) {
5001 if (dataBufSize >= bufsize) {
5002 iocmd.flags &= ~MPT_ICFLAG_ECHO;
5003 patt = -1;
5004 continue;
5005 }
5006 }
5007 goto target_done;
5008 }
5009 else
5010 goto target_done;
5011 } else {
5012 /* fatal error */
5013 goto target_done;
5014 }
5015 }
5016
5017 iocmd.cmd = READ_BUFFER;
5018 iocmd.data_dma = buf2_dma;
5019 iocmd.data = pbuf2;
5020 iocmd.size = sz;
5021 if (mptscsih_do_cmd(hd, &iocmd) < 0)
5022 goto target_done;
5023 else if (hd->pLocal == NULL)
5024 goto target_done;
5025 else {
5026 rc = hd->pLocal->completion;
5027 if (rc == MPT_SCANDV_GOOD) {
5028 /* If buffers compare,
5029 * go to next pattern,
5030 * else, do a fallback and restart
5031 * data transfer test.
5032 */
5033 if (memcmp (pbuf1, pbuf2, sz) == 0) {
5034 ; /* goto next pattern */
5035 } else {
5036 /* Miscompare with Echo buffer, go to data buffer,
5037 * if that buffer exists.
5038 * Miscompare with Data buffer, check first 4 bytes,
5039 * some devices return capacity. Exit in this case.
5040 */
5041 if (iocmd.flags & MPT_ICFLAG_ECHO) {
5042 if (dataBufSize >= bufsize)
5043 iocmd.flags &= ~MPT_ICFLAG_ECHO;
5044 else
5045 goto target_done;
5046 } else {
5047 if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) {
5048 /* Argh. Device returning wrong data.
5049 * Quit DV for this device.
5050 */
5051 goto target_done;
5052 }
5053
5054 /* Had an actual miscompare. Slow down.*/
5055 dv.cmd = MPT_FALLBACK;
5056 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5057
5058 if (mpt_config(hd->ioc, &cfg) != 0)
5059 goto target_done;
5060
5061 if ((!dv.now.width) && (!dv.now.offset))
5062 goto target_done;
5063 }
5064
5065 patt = -1;
5066 continue;
5067 }
5068 } else if (rc == MPT_SCANDV_DID_RESET) {
5069 /* Do Fallback and restart
5070 * this test (re-issue reserve
5071 * because of bus reset).
5072 */
5073 dv.cmd = MPT_FALLBACK;
5074 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5075
5076 if (mpt_config(hd->ioc, &cfg) != 0)
5077 goto target_done;
5078
5079 if ((!dv.now.width) && (!dv.now.offset))
5080 goto target_done;
5081
5082 iocmd.flags |= MPT_ICFLAG_DID_RESET;
5083 patt = -1;
5084 continue;
5085 } else if (rc == MPT_SCANDV_SENSE) {
5086 /* Restart data test if UA, else quit.
5087 */
5088 u8 skey = hd->pLocal->sense[2] & 0x0F;
5089 ddvprintk((MYIOC_s_INFO_FMT
5090 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
5091 hd->pLocal->sense[12], hd->pLocal->sense[13]));
5092 if (skey == UNIT_ATTENTION) {
5093 patt = -1;
5094 continue;
5095 }
5096 else
5097 goto target_done;
5098 } else {
5099 /* fatal error */
5100 goto target_done;
5101 }
5102 }
5103
5104 } /* --- end of patt loop ---- */
5105
5106target_done:
5107 if (iocmd.flags & MPT_ICFLAG_RESERVED) {
5108 iocmd.cmd = RELEASE;
5109 iocmd.data_dma = -1;
5110 iocmd.data = NULL;
5111 iocmd.size = 0;
5112 if (mptscsih_do_cmd(hd, &iocmd) < 0)
5113 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5114 ioc->name, id);
5115 else if (hd->pLocal) {
5116 if (hd->pLocal->completion == MPT_SCANDV_GOOD)
5117 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
5118 } else {
5119 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5120 ioc->name, id);
5121 }
5122 }
5123
5124
5125 /* Set if cfg1_dma_addr contents is valid
5126 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005127 if ((cfg.cfghdr.hdr != NULL) && (retcode == 0)){
Linus Torvalds1da177e2005-04-16 15:20:36 -07005128 /* If disk, not U320, disable QAS
5129 */
5130 if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) {
5131 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02005132 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07005133 "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor));
5134 }
5135
5136 dv.cmd = MPT_SAVE;
5137 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5138
5139 /* Double writes to SDP1 can cause problems,
5140 * skip save of the final negotiated settings to
5141 * SCSI device page 1.
5142 *
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005143 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005144 cfg.physAddr = cfg1_dma_addr;
5145 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
5146 cfg.dir = 1;
5147 mpt_config(hd->ioc, &cfg);
5148 */
5149 }
5150
5151 /* If this is a RAID Passthrough, enable internal IOs
5152 */
5153 if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) {
5154 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0)
5155 ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name));
5156 }
5157
5158 /* Done with the DV scan of the current target
5159 */
5160 if (pDvBuf)
5161 pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma);
5162
5163 ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n",
5164 ioc->name, id));
5165
5166 return retcode;
5167}
5168
5169/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5170/* mptscsih_dv_parms - perform a variety of operations on the
5171 * parameters used for negotiation.
5172 * @hd: Pointer to a SCSI host.
5173 * @dv: Pointer to a structure that contains the maximum and current
5174 * negotiated parameters.
5175 */
5176static void
5177mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage)
5178{
5179 VirtDevice *pTarget;
5180 SCSIDevicePage0_t *pPage0;
5181 SCSIDevicePage1_t *pPage1;
5182 int val = 0, data, configuration;
5183 u8 width = 0;
5184 u8 offset = 0;
5185 u8 factor = 0;
5186 u8 negoFlags = 0;
5187 u8 cmd = dv->cmd;
5188 u8 id = dv->id;
5189
5190 switch (cmd) {
5191 case MPT_GET_NVRAM_VALS:
5192 ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ",
5193 hd->ioc->name));
5194 /* Get the NVRAM values and save in tmax
5195 * If not an LVD bus, the adapter minSyncFactor has been
5196 * already throttled back.
5197 */
5198 if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume) {
5199 width = pTarget->maxWidth;
5200 offset = pTarget->maxOffset;
5201 factor = pTarget->minSyncFactor;
5202 negoFlags = pTarget->negoFlags;
5203 } else {
5204 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
5205 data = hd->ioc->spi_data.nvram[id];
5206 width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
5207 if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0)
5208 factor = MPT_ASYNC;
5209 else {
5210 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
5211 if ((factor == 0) || (factor == MPT_ASYNC)){
5212 factor = MPT_ASYNC;
5213 offset = 0;
5214 }
5215 }
5216 } else {
5217 width = MPT_NARROW;
5218 offset = 0;
5219 factor = MPT_ASYNC;
5220 }
5221
5222 /* Set the negotiation flags */
5223 negoFlags = hd->ioc->spi_data.noQas;
5224 if (!width)
5225 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
5226
5227 if (!offset)
5228 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
5229 }
5230
5231 /* limit by adapter capabilities */
5232 width = min(width, hd->ioc->spi_data.maxBusWidth);
5233 offset = min(offset, hd->ioc->spi_data.maxSyncOffset);
5234 factor = max(factor, hd->ioc->spi_data.minSyncFactor);
5235
5236 /* Check Consistency */
5237 if (offset && (factor < MPT_ULTRA2) && !width)
5238 factor = MPT_ULTRA2;
5239
5240 dv->max.width = width;
5241 dv->max.offset = offset;
5242 dv->max.factor = factor;
5243 dv->max.flags = negoFlags;
5244 ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n",
5245 id, width, factor, offset, negoFlags));
5246 break;
5247
5248 case MPT_UPDATE_MAX:
5249 ddvprintk((MYIOC_s_NOTE_FMT
5250 "Updating with SDP0 Data: ", hd->ioc->name));
5251 /* Update tmax values with those from Device Page 0.*/
5252 pPage0 = (SCSIDevicePage0_t *) pPage;
5253 if (pPage0) {
Christoph Hellwig637fa992005-08-18 16:25:44 +02005254 val = le32_to_cpu(pPage0->NegotiatedParameters);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005255 dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0;
5256 dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16;
5257 dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8;
5258 }
5259
5260 dv->now.width = dv->max.width;
5261 dv->now.offset = dv->max.offset;
5262 dv->now.factor = dv->max.factor;
5263 ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n",
5264 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5265 break;
5266
5267 case MPT_SET_MAX:
5268 ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ",
5269 hd->ioc->name));
5270 /* Set current to the max values. Update the config page.*/
5271 dv->now.width = dv->max.width;
5272 dv->now.offset = dv->max.offset;
5273 dv->now.factor = dv->max.factor;
5274 dv->now.flags = dv->max.flags;
5275
5276 pPage1 = (SCSIDevicePage1_t *)pPage;
5277 if (pPage1) {
5278 mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor,
5279 dv->now.offset, &val, &configuration, dv->now.flags);
5280 dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5281 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005282 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005283 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005284 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005285 }
5286
Christoph Hellwig637fa992005-08-18 16:25:44 +02005287 ddvprintk(("id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x configuration=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005288 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
5289 break;
5290
5291 case MPT_SET_MIN:
5292 ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
5293 hd->ioc->name));
5294 /* Set page to asynchronous and narrow
5295 * Do not update now, breaks fallback routine. */
5296 width = MPT_NARROW;
5297 offset = 0;
5298 factor = MPT_ASYNC;
5299 negoFlags = dv->max.flags;
5300
5301 pPage1 = (SCSIDevicePage1_t *)pPage;
5302 if (pPage1) {
5303 mptscsih_setDevicePage1Flags (width, factor,
5304 offset, &val, &configuration, negoFlags);
5305 dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5306 id, width, factor, offset, negoFlags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005307 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005308 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005309 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005310 }
5311 ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n",
5312 id, width, factor, offset, val, configuration, negoFlags));
5313 break;
5314
5315 case MPT_FALLBACK:
5316 ddvprintk((MYIOC_s_NOTE_FMT
5317 "Fallback: Start: offset %d, factor %x, width %d \n",
5318 hd->ioc->name, dv->now.offset,
5319 dv->now.factor, dv->now.width));
5320 width = dv->now.width;
5321 offset = dv->now.offset;
5322 factor = dv->now.factor;
5323 if ((offset) && (dv->max.width)) {
5324 if (factor < MPT_ULTRA160)
5325 factor = MPT_ULTRA160;
5326 else if (factor < MPT_ULTRA2) {
5327 factor = MPT_ULTRA2;
5328 width = MPT_WIDE;
5329 } else if ((factor == MPT_ULTRA2) && width) {
5330 factor = MPT_ULTRA2;
5331 width = MPT_NARROW;
5332 } else if (factor < MPT_ULTRA) {
5333 factor = MPT_ULTRA;
5334 width = MPT_WIDE;
5335 } else if ((factor == MPT_ULTRA) && width) {
5336 width = MPT_NARROW;
5337 } else if (factor < MPT_FAST) {
5338 factor = MPT_FAST;
5339 width = MPT_WIDE;
5340 } else if ((factor == MPT_FAST) && width) {
5341 factor = MPT_FAST;
5342 width = MPT_NARROW;
5343 } else if (factor < MPT_SCSI) {
5344 factor = MPT_SCSI;
5345 width = MPT_WIDE;
5346 } else if ((factor == MPT_SCSI) && width) {
5347 factor = MPT_SCSI;
5348 width = MPT_NARROW;
5349 } else {
5350 factor = MPT_ASYNC;
5351 offset = 0;
5352 }
5353
5354 } else if (offset) {
5355 width = MPT_NARROW;
5356 if (factor < MPT_ULTRA)
5357 factor = MPT_ULTRA;
5358 else if (factor < MPT_FAST)
5359 factor = MPT_FAST;
5360 else if (factor < MPT_SCSI)
5361 factor = MPT_SCSI;
5362 else {
5363 factor = MPT_ASYNC;
5364 offset = 0;
5365 }
5366
5367 } else {
5368 width = MPT_NARROW;
5369 factor = MPT_ASYNC;
5370 }
5371 dv->max.flags |= MPT_TARGET_NO_NEGO_QAS;
5372 dv->max.flags &= ~MPT_TAPE_NEGO_IDP;
5373
5374 dv->now.width = width;
5375 dv->now.offset = offset;
5376 dv->now.factor = factor;
5377 dv->now.flags = dv->max.flags;
5378
5379 pPage1 = (SCSIDevicePage1_t *)pPage;
5380 if (pPage1) {
5381 mptscsih_setDevicePage1Flags (width, factor, offset, &val,
5382 &configuration, dv->now.flags);
Christoph Hellwig637fa992005-08-18 16:25:44 +02005383 dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x negoFlags=%x request=%x config=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005384 id, width, offset, factor, dv->now.flags, val, configuration));
5385
Christoph Hellwig637fa992005-08-18 16:25:44 +02005386 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005387 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005388 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005389 }
5390
5391 ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n",
5392 id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration));
5393 break;
5394
5395 case MPT_SAVE:
5396 ddvprintk((MYIOC_s_NOTE_FMT
5397 "Saving to Target structure: ", hd->ioc->name));
5398 ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n",
5399 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5400
5401 /* Save these values to target structures
5402 * or overwrite nvram (phys disks only).
5403 */
5404
5405 if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume ) {
5406 pTarget->maxWidth = dv->now.width;
5407 pTarget->maxOffset = dv->now.offset;
5408 pTarget->minSyncFactor = dv->now.factor;
5409 pTarget->negoFlags = dv->now.flags;
5410 } else {
5411 /* Preserv all flags, use
5412 * read-modify-write algorithm
5413 */
5414 if (hd->ioc->spi_data.nvram) {
5415 data = hd->ioc->spi_data.nvram[id];
5416
5417 if (dv->now.width)
5418 data &= ~MPT_NVRAM_WIDE_DISABLE;
5419 else
5420 data |= MPT_NVRAM_WIDE_DISABLE;
5421
5422 if (!dv->now.offset)
5423 factor = MPT_ASYNC;
5424
5425 data &= ~MPT_NVRAM_SYNC_MASK;
5426 data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK;
5427
5428 hd->ioc->spi_data.nvram[id] = data;
5429 }
5430 }
5431 break;
5432 }
5433}
5434
5435/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5436/* mptscsih_fillbuf - fill a buffer with a special data pattern
5437 * cleanup. For bus scan only.
5438 *
5439 * @buffer: Pointer to data buffer to be filled.
5440 * @size: Number of bytes to fill
5441 * @index: Pattern index
5442 * @width: bus width, 0 (8 bits) or 1 (16 bits)
5443 */
5444static void
5445mptscsih_fillbuf(char *buffer, int size, int index, int width)
5446{
5447 char *ptr = buffer;
5448 int ii;
5449 char byte;
5450 short val;
5451
5452 switch (index) {
5453 case 0:
5454
5455 if (width) {
5456 /* Pattern: 0000 FFFF 0000 FFFF
5457 */
5458 for (ii=0; ii < size; ii++, ptr++) {
5459 if (ii & 0x02)
5460 *ptr = 0xFF;
5461 else
5462 *ptr = 0x00;
5463 }
5464 } else {
5465 /* Pattern: 00 FF 00 FF
5466 */
5467 for (ii=0; ii < size; ii++, ptr++) {
5468 if (ii & 0x01)
5469 *ptr = 0xFF;
5470 else
5471 *ptr = 0x00;
5472 }
5473 }
5474 break;
5475
5476 case 1:
5477 if (width) {
5478 /* Pattern: 5555 AAAA 5555 AAAA 5555
5479 */
5480 for (ii=0; ii < size; ii++, ptr++) {
5481 if (ii & 0x02)
5482 *ptr = 0xAA;
5483 else
5484 *ptr = 0x55;
5485 }
5486 } else {
5487 /* Pattern: 55 AA 55 AA 55
5488 */
5489 for (ii=0; ii < size; ii++, ptr++) {
5490 if (ii & 0x01)
5491 *ptr = 0xAA;
5492 else
5493 *ptr = 0x55;
5494 }
5495 }
5496 break;
5497
5498 case 2:
5499 /* Pattern: 00 01 02 03 04 05
5500 * ... FE FF 00 01..
5501 */
5502 for (ii=0; ii < size; ii++, ptr++)
5503 *ptr = (char) ii;
5504 break;
5505
5506 case 3:
5507 if (width) {
5508 /* Wide Pattern: FFFE 0001 FFFD 0002
5509 * ... 4000 DFFF 8000 EFFF
5510 */
5511 byte = 0;
5512 for (ii=0; ii < size/2; ii++) {
5513 /* Create the base pattern
5514 */
5515 val = (1 << byte);
5516 /* every 64 (0x40) bytes flip the pattern
5517 * since we fill 2 bytes / iteration,
5518 * test for ii = 0x20
5519 */
5520 if (ii & 0x20)
5521 val = ~(val);
5522
5523 if (ii & 0x01) {
5524 *ptr = (char)( (val & 0xFF00) >> 8);
5525 ptr++;
5526 *ptr = (char)(val & 0xFF);
5527 byte++;
5528 byte &= 0x0F;
5529 } else {
5530 val = ~val;
5531 *ptr = (char)( (val & 0xFF00) >> 8);
5532 ptr++;
5533 *ptr = (char)(val & 0xFF);
5534 }
5535
5536 ptr++;
5537 }
5538 } else {
5539 /* Narrow Pattern: FE 01 FD 02 FB 04
5540 * .. 7F 80 01 FE 02 FD ... 80 7F
5541 */
5542 byte = 0;
5543 for (ii=0; ii < size; ii++, ptr++) {
5544 /* Base pattern - first 32 bytes
5545 */
5546 if (ii & 0x01) {
5547 *ptr = (1 << byte);
5548 byte++;
5549 byte &= 0x07;
5550 } else {
5551 *ptr = (char) (~(1 << byte));
5552 }
5553
5554 /* Flip the pattern every 32 bytes
5555 */
5556 if (ii & 0x20)
5557 *ptr = ~(*ptr);
5558 }
5559 }
5560 break;
5561 }
5562}
5563#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
5564
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005565EXPORT_SYMBOL(mptscsih_remove);
5566EXPORT_SYMBOL(mptscsih_shutdown);
5567#ifdef CONFIG_PM
5568EXPORT_SYMBOL(mptscsih_suspend);
5569EXPORT_SYMBOL(mptscsih_resume);
5570#endif
5571EXPORT_SYMBOL(mptscsih_proc_info);
5572EXPORT_SYMBOL(mptscsih_info);
5573EXPORT_SYMBOL(mptscsih_qcmd);
5574EXPORT_SYMBOL(mptscsih_slave_alloc);
5575EXPORT_SYMBOL(mptscsih_slave_destroy);
5576EXPORT_SYMBOL(mptscsih_slave_configure);
5577EXPORT_SYMBOL(mptscsih_abort);
5578EXPORT_SYMBOL(mptscsih_dev_reset);
5579EXPORT_SYMBOL(mptscsih_bus_reset);
5580EXPORT_SYMBOL(mptscsih_host_reset);
5581EXPORT_SYMBOL(mptscsih_bios_param);
5582EXPORT_SYMBOL(mptscsih_io_done);
5583EXPORT_SYMBOL(mptscsih_taskmgmt_complete);
5584EXPORT_SYMBOL(mptscsih_scandv_complete);
5585EXPORT_SYMBOL(mptscsih_event_process);
5586EXPORT_SYMBOL(mptscsih_ioc_reset);
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06005587EXPORT_SYMBOL(mptscsih_change_queue_depth);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005588EXPORT_SYMBOL(mptscsih_timer_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005589
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005590/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/