blob: 4a003dc5fde86084b66cd8316467c3fe1e5f2d57 [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;
1259 VirtDevice *pTarget;
1260 int target;
1261 int lun;
1262 u32 datalen;
1263 u32 scsictl;
1264 u32 scsidir;
1265 u32 cmd_len;
1266 int my_idx;
1267 int ii;
1268
1269 hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata;
1270 target = SCpnt->device->id;
1271 lun = SCpnt->device->lun;
1272 SCpnt->scsi_done = done;
1273
1274 pTarget = hd->Targets[target];
1275
1276 dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
1277 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
1278
1279 if (hd->resetPending) {
1280 dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
1281 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
1282 return SCSI_MLQUEUE_HOST_BUSY;
1283 }
1284
1285 /*
1286 * Put together a MPT SCSI request...
1287 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001288 if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
1290 hd->ioc->name));
1291 return SCSI_MLQUEUE_HOST_BUSY;
1292 }
1293
1294 pScsiReq = (SCSIIORequest_t *) mf;
1295
1296 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
1297
1298 ADD_INDEX_LOG(my_idx);
1299
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001300 /* TUR's being issued with scsictl=0x02000000 (DATA_IN)!
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 * Seems we may receive a buffer (datalen>0) even when there
1302 * will be no data transfer! GRRRRR...
1303 */
1304 if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
1305 datalen = SCpnt->request_bufflen;
1306 scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
1307 } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
1308 datalen = SCpnt->request_bufflen;
1309 scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
1310 } else {
1311 datalen = 0;
1312 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
1313 }
1314
1315 /* Default to untagged. Once a target structure has been allocated,
1316 * use the Inquiry data to determine if device supports tagged.
1317 */
1318 if ( pTarget
1319 && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
1320 && (SCpnt->device->tagged_supported)) {
1321 scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
1322 } else {
1323 scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
1324 }
1325
1326 /* Use the above information to set up the message frame
1327 */
1328 pScsiReq->TargetID = (u8) target;
1329 pScsiReq->Bus = (u8) SCpnt->device->channel;
1330 pScsiReq->ChainOffset = 0;
1331 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
1332 pScsiReq->CDBLength = SCpnt->cmd_len;
1333 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
1334 pScsiReq->Reserved = 0;
1335 pScsiReq->MsgFlags = mpt_msg_flags();
1336 pScsiReq->LUN[0] = 0;
1337 pScsiReq->LUN[1] = lun;
1338 pScsiReq->LUN[2] = 0;
1339 pScsiReq->LUN[3] = 0;
1340 pScsiReq->LUN[4] = 0;
1341 pScsiReq->LUN[5] = 0;
1342 pScsiReq->LUN[6] = 0;
1343 pScsiReq->LUN[7] = 0;
1344 pScsiReq->Control = cpu_to_le32(scsictl);
1345
1346 /*
1347 * Write SCSI CDB into the message
1348 */
1349 cmd_len = SCpnt->cmd_len;
1350 for (ii=0; ii < cmd_len; ii++)
1351 pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
1352
1353 for (ii=cmd_len; ii < 16; ii++)
1354 pScsiReq->CDB[ii] = 0;
1355
1356 /* DataLength */
1357 pScsiReq->DataLength = cpu_to_le32(datalen);
1358
1359 /* SenseBuffer low address */
1360 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
1361 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
1362
1363 /* Now add the SG list
1364 * Always have a SGE even if null length.
1365 */
1366 if (datalen == 0) {
1367 /* Add a NULL SGE */
1368 mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
1369 (dma_addr_t) -1);
1370 } else {
1371 /* Add a 32 or 64 bit SGE */
1372 if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS)
1373 goto fail;
1374 }
1375
1376 hd->ScsiLookup[my_idx] = SCpnt;
1377 SCpnt->host_scribble = NULL;
1378
1379#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1380 if (hd->ioc->bus_type == SCSI) {
1381 int dvStatus = hd->ioc->spi_data.dvStatus[target];
1382 int issueCmd = 1;
1383
1384 if (dvStatus || hd->ioc->spi_data.forceDv) {
1385
1386 if ((dvStatus & MPT_SCSICFG_NEED_DV) ||
1387 (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) {
1388 unsigned long lflags;
1389 /* Schedule DV if necessary */
1390 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1391 if (!dvtaskQ_active) {
1392 dvtaskQ_active = 1;
1393 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001394 INIT_WORK(&dvTaskQ_task, mptscsih_domainValidation, (void *) hd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001396 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 } else {
1398 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1399 }
1400 hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV;
1401 }
1402
1403 /* Trying to do DV to this target, extend timeout.
1404 * Wait to issue until flag is clear
1405 */
1406 if (dvStatus & MPT_SCSICFG_DV_PENDING) {
1407 mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
1408 issueCmd = 0;
1409 }
1410
1411 /* Set the DV flags.
1412 */
1413 if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
1414 mptscsih_set_dvflags(hd, pScsiReq);
1415
1416 if (!issueCmd)
1417 goto fail;
1418 }
1419 }
1420#endif
1421
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001422 mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
1424 hd->ioc->name, SCpnt, mf, my_idx));
1425 DBG_DUMP_REQUEST_FRAME(mf)
1426 return 0;
1427
1428 fail:
1429 mptscsih_freeChainBuffers(hd->ioc, my_idx);
1430 mpt_free_msg_frame(hd->ioc, mf);
1431 return SCSI_MLQUEUE_HOST_BUSY;
1432}
1433
1434/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1435/*
1436 * mptscsih_freeChainBuffers - Function to free chain buffers associated
1437 * with a SCSI IO request
1438 * @hd: Pointer to the MPT_SCSI_HOST instance
1439 * @req_idx: Index of the SCSI IO request frame.
1440 *
1441 * Called if SG chain buffer allocation fails and mptscsih callbacks.
1442 * No return.
1443 */
1444static void
1445mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
1446{
1447 MPT_FRAME_HDR *chain;
1448 unsigned long flags;
1449 int chain_idx;
1450 int next;
1451
1452 /* Get the first chain index and reset
1453 * tracker state.
1454 */
1455 chain_idx = ioc->ReqToChain[req_idx];
1456 ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
1457
1458 while (chain_idx != MPT_HOST_NO_CHAIN) {
1459
1460 /* Save the next chain buffer index */
1461 next = ioc->ChainToChain[chain_idx];
1462
1463 /* Free this chain buffer and reset
1464 * tracker
1465 */
1466 ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
1467
1468 chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer
1469 + (chain_idx * ioc->req_sz));
1470
1471 spin_lock_irqsave(&ioc->FreeQlock, flags);
1472 list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ);
1473 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
1474
1475 dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
1476 ioc->name, chain_idx));
1477
1478 /* handle next */
1479 chain_idx = next;
1480 }
1481 return;
1482}
1483
1484/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1485/*
1486 * Reset Handling
1487 */
1488
1489/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1490/*
1491 * mptscsih_TMHandler - Generic handler for SCSI Task Management.
1492 * Fall through to mpt_HardResetHandler if: not operational, too many
1493 * failed TM requests or handshake failure.
1494 *
1495 * @ioc: Pointer to MPT_ADAPTER structure
1496 * @type: Task Management type
1497 * @target: Logical Target ID for reset (if appropriate)
1498 * @lun: Logical Unit for reset (if appropriate)
1499 * @ctx2abort: Context for the task to be aborted (if appropriate)
1500 *
1501 * Remark: Currently invoked from a non-interrupt thread (_bh).
1502 *
1503 * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
1504 * will be active.
1505 *
1506 * Returns 0 for SUCCESS or -1 if FAILED.
1507 */
1508static int
1509mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1510{
1511 MPT_ADAPTER *ioc;
1512 int rc = -1;
1513 int doTask = 1;
1514 u32 ioc_raw_state;
1515 unsigned long flags;
1516
1517 /* If FW is being reloaded currently, return success to
1518 * the calling function.
1519 */
1520 if (hd == NULL)
1521 return 0;
1522
1523 ioc = hd->ioc;
1524 if (ioc == NULL) {
1525 printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
1526 return FAILED;
1527 }
1528 dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
1529
1530 // SJR - CHECKME - Can we avoid this here?
1531 // (mpt_HardResetHandler has this check...)
1532 spin_lock_irqsave(&ioc->diagLock, flags);
1533 if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
1534 spin_unlock_irqrestore(&ioc->diagLock, flags);
1535 return FAILED;
1536 }
1537 spin_unlock_irqrestore(&ioc->diagLock, flags);
1538
1539 /* Wait a fixed amount of time for the TM pending flag to be cleared.
1540 * If we time out and not bus reset, then we return a FAILED status to the caller.
1541 * The call to mptscsih_tm_pending_wait() will set the pending flag if we are
1542 * successful. Otherwise, reload the FW.
1543 */
1544 if (mptscsih_tm_pending_wait(hd) == FAILED) {
1545 if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001546 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 "Timed out waiting for last TM (%d) to complete! \n",
1548 hd->ioc->name, hd->tmPending));
1549 return FAILED;
1550 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001551 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 "Timed out waiting for last TM (%d) to complete! \n",
1553 hd->ioc->name, hd->tmPending));
1554 return FAILED;
1555 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001556 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 "Timed out waiting for last TM (%d) to complete! \n",
1558 hd->ioc->name, hd->tmPending));
1559 if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
1560 return FAILED;
1561
1562 doTask = 0;
1563 }
1564 } else {
1565 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1566 hd->tmPending |= (1 << type);
1567 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1568 }
1569
1570 /* Is operational?
1571 */
1572 ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
1573
1574#ifdef MPT_DEBUG_RESET
1575 if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
1576 printk(MYIOC_s_WARN_FMT
1577 "TM Handler: IOC Not operational(0x%x)!\n",
1578 hd->ioc->name, ioc_raw_state);
1579 }
1580#endif
1581
1582 if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
1583 && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
1584
1585 /* Isse the Task Mgmt request.
1586 */
1587 if (hd->hard_resets < -1)
1588 hd->hard_resets++;
1589 rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout);
1590 if (rc) {
1591 printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
1592 } else {
1593 dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
1594 }
1595 }
1596
1597 /* Only fall through to the HRH if this is a bus reset
1598 */
1599 if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
1600 ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
1601 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1602 hd->ioc->name));
1603 rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1604 }
1605
1606 dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
1607
1608 return rc;
1609}
1610
1611
1612/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1613/*
1614 * mptscsih_IssueTaskMgmt - Generic send Task Management function.
1615 * @hd: Pointer to MPT_SCSI_HOST structure
1616 * @type: Task Management type
1617 * @target: Logical Target ID for reset (if appropriate)
1618 * @lun: Logical Unit for reset (if appropriate)
1619 * @ctx2abort: Context for the task to be aborted (if appropriate)
1620 *
1621 * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
1622 * or a non-interrupt thread. In the former, must not call schedule().
1623 *
1624 * Not all fields are meaningfull for all task types.
1625 *
1626 * Returns 0 for SUCCESS, -999 for "no msg frames",
1627 * else other non-zero value returned.
1628 */
1629static int
1630mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1631{
1632 MPT_FRAME_HDR *mf;
1633 SCSITaskMgmt_t *pScsiTm;
1634 int ii;
1635 int retval;
1636
1637 /* Return Fail to calling function if no message frames available.
1638 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001639 if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
1641 hd->ioc->name));
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001642 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 }
1644 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
1645 hd->ioc->name, mf));
1646
1647 /* Format the Request
1648 */
1649 pScsiTm = (SCSITaskMgmt_t *) mf;
1650 pScsiTm->TargetID = target;
1651 pScsiTm->Bus = channel;
1652 pScsiTm->ChainOffset = 0;
1653 pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1654
1655 pScsiTm->Reserved = 0;
1656 pScsiTm->TaskType = type;
1657 pScsiTm->Reserved1 = 0;
1658 pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
1659 ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
1660
1661 for (ii= 0; ii < 8; ii++) {
1662 pScsiTm->LUN[ii] = 0;
1663 }
1664 pScsiTm->LUN[1] = lun;
1665
1666 for (ii=0; ii < 7; ii++)
1667 pScsiTm->Reserved2[ii] = 0;
1668
1669 pScsiTm->TaskMsgContext = ctx2abort;
1670
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001671 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n",
1672 hd->ioc->name, ctx2abort, type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
1674 DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm);
1675
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001676 if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 sizeof(SCSITaskMgmt_t), (u32*)pScsiTm,
1678 CAN_SLEEP)) != 0) {
1679 dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!"
1680 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1681 hd->ioc, mf));
1682 mpt_free_msg_frame(hd->ioc, mf);
1683 return retval;
1684 }
1685
1686 if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
1687 dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!"
1688 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1689 hd->ioc, mf));
1690 mpt_free_msg_frame(hd->ioc, mf);
1691 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1692 hd->ioc->name));
1693 retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1694 }
1695
1696 return retval;
1697}
1698
1699/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1700/**
1701 * mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant
1702 * @SCpnt: Pointer to scsi_cmnd structure, IO to be aborted
1703 *
1704 * (linux scsi_host_template.eh_abort_handler routine)
1705 *
1706 * Returns SUCCESS or FAILED.
1707 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001708int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709mptscsih_abort(struct scsi_cmnd * SCpnt)
1710{
1711 MPT_SCSI_HOST *hd;
1712 MPT_ADAPTER *ioc;
1713 MPT_FRAME_HDR *mf;
1714 u32 ctx2abort;
1715 int scpnt_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
1717 /* If we can't locate our host adapter structure, return FAILED status.
1718 */
1719 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) {
1720 SCpnt->result = DID_RESET << 16;
1721 SCpnt->scsi_done(SCpnt);
1722 dfailprintk((KERN_WARNING MYNAM ": mptscsih_abort: "
1723 "Can't locate host! (sc=%p)\n",
1724 SCpnt));
1725 return FAILED;
1726 }
1727
1728 ioc = hd->ioc;
1729 if (hd->resetPending)
1730 return FAILED;
1731
1732 printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)\n",
1733 hd->ioc->name, SCpnt);
1734
1735 if (hd->timeouts < -1)
1736 hd->timeouts++;
1737
1738 /* Find this command
1739 */
1740 if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
1741 /* Cmd not found in ScsiLookup.
1742 * Do OS callback.
1743 */
1744 SCpnt->result = DID_RESET << 16;
1745 dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
1746 "Command not in the active list! (sc=%p)\n",
1747 hd->ioc->name, SCpnt));
1748 return SUCCESS;
1749 }
1750
1751 /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
1752 * (the IO to be ABORT'd)
1753 *
1754 * NOTE: Since we do not byteswap MsgContext, we do not
1755 * swap it here either. It is an opaque cookie to
1756 * the controller, so it does not matter. -DaveM
1757 */
1758 mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
1759 ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
1760
1761 hd->abortSCpnt = SCpnt;
1762
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
1764 SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun,
1765 ctx2abort, 2 /* 2 second timeout */)
1766 < 0) {
1767
1768 /* The TM request failed and the subsequent FW-reload failed!
1769 * Fatal error case.
1770 */
1771 printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)\n",
1772 hd->ioc->name, SCpnt);
1773
1774 /* We must clear our pending flag before clearing our state.
1775 */
1776 hd->tmPending = 0;
1777 hd->tmState = TM_STATE_NONE;
1778
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 /* Unmap the DMA buffers, if any. */
1780 if (SCpnt->use_sg) {
1781 pci_unmap_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer,
1782 SCpnt->use_sg, SCpnt->sc_data_direction);
1783 } else if (SCpnt->request_bufflen) {
1784 pci_unmap_single(ioc->pcidev, SCpnt->SCp.dma_handle,
1785 SCpnt->request_bufflen, SCpnt->sc_data_direction);
1786 }
1787 hd->ScsiLookup[scpnt_idx] = NULL;
1788 SCpnt->result = DID_RESET << 16;
1789 SCpnt->scsi_done(SCpnt); /* Issue the command callback */
1790 mptscsih_freeChainBuffers(ioc, scpnt_idx);
1791 mpt_free_msg_frame(ioc, mf);
1792 return FAILED;
1793 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 return SUCCESS;
1795}
1796
1797/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1798/**
1799 * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
1800 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1801 *
1802 * (linux scsi_host_template.eh_dev_reset_handler routine)
1803 *
1804 * Returns SUCCESS or FAILED.
1805 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001806int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
1808{
1809 MPT_SCSI_HOST *hd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810
1811 /* If we can't locate our host adapter structure, return FAILED status.
1812 */
1813 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
1814 dtmprintk((KERN_WARNING MYNAM ": mptscsih_dev_reset: "
1815 "Can't locate host! (sc=%p)\n",
1816 SCpnt));
1817 return FAILED;
1818 }
1819
1820 if (hd->resetPending)
1821 return FAILED;
1822
1823 printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n",
1824 hd->ioc->name, SCpnt);
1825
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
1827 SCpnt->device->channel, SCpnt->device->id,
1828 0, 0, 5 /* 5 second timeout */)
1829 < 0){
1830 /* The TM request failed and the subsequent FW-reload failed!
1831 * Fatal error case.
1832 */
1833 printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)\n",
1834 hd->ioc->name, SCpnt);
1835 hd->tmPending = 0;
1836 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 return FAILED;
1838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839
Jeff Garzik 94d0e7b2005-05-28 07:55:48 -04001840 return SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841}
1842
1843/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1844/**
1845 * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
1846 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1847 *
1848 * (linux scsi_host_template.eh_bus_reset_handler routine)
1849 *
1850 * Returns SUCCESS or FAILED.
1851 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001852int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
1854{
1855 MPT_SCSI_HOST *hd;
1856 spinlock_t *host_lock = SCpnt->device->host->host_lock;
1857
1858 /* If we can't locate our host adapter structure, return FAILED status.
1859 */
1860 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
1861 dtmprintk((KERN_WARNING MYNAM ": mptscsih_bus_reset: "
1862 "Can't locate host! (sc=%p)\n",
1863 SCpnt ) );
1864 return FAILED;
1865 }
1866
1867 printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)\n",
1868 hd->ioc->name, SCpnt);
1869
1870 if (hd->timeouts < -1)
1871 hd->timeouts++;
1872
1873 /* We are now ready to execute the task management request. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
1875 SCpnt->device->channel, 0, 0, 0, 5 /* 5 second timeout */)
1876 < 0){
1877
1878 /* The TM request failed and the subsequent FW-reload failed!
1879 * Fatal error case.
1880 */
1881 printk(MYIOC_s_WARN_FMT
1882 "Error processing TaskMgmt request (sc=%p)\n",
1883 hd->ioc->name, SCpnt);
1884 hd->tmPending = 0;
1885 hd->tmState = TM_STATE_NONE;
1886 spin_lock_irq(host_lock);
1887 return FAILED;
1888 }
Jeff Garzik 68b3aa72005-05-28 07:56:31 -04001889
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 return SUCCESS;
1891}
1892
1893/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1894/**
1895 * mptscsih_host_reset - Perform a SCSI host adapter RESET!
1896 * new_eh variant
1897 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1898 *
1899 * (linux scsi_host_template.eh_host_reset_handler routine)
1900 *
1901 * Returns SUCCESS or FAILED.
1902 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001903int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904mptscsih_host_reset(struct scsi_cmnd *SCpnt)
1905{
1906 MPT_SCSI_HOST * hd;
1907 int status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
1909 /* If we can't locate the host to reset, then we failed. */
1910 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001911 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 "Can't locate host! (sc=%p)\n",
1913 SCpnt ) );
1914 return FAILED;
1915 }
1916
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001917 printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 hd->ioc->name, SCpnt);
1919
1920 /* If our attempts to reset the host failed, then return a failed
1921 * status. The host will be taken off line by the SCSI mid-layer.
1922 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
1924 status = FAILED;
1925 } else {
1926 /* Make sure TM pending is cleared and TM state is set to
1927 * NONE.
1928 */
1929 hd->tmPending = 0;
1930 hd->tmState = TM_STATE_NONE;
1931 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001933 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 "Status = %s\n",
1935 (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
1936
1937 return status;
1938}
1939
1940/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1941/**
1942 * mptscsih_tm_pending_wait - wait for pending task management request to
1943 * complete.
1944 * @hd: Pointer to MPT host structure.
1945 *
1946 * Returns {SUCCESS,FAILED}.
1947 */
1948static int
1949mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
1950{
1951 unsigned long flags;
1952 int loop_count = 4 * 10; /* Wait 10 seconds */
1953 int status = FAILED;
1954
1955 do {
1956 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1957 if (hd->tmState == TM_STATE_NONE) {
1958 hd->tmState = TM_STATE_IN_PROGRESS;
1959 hd->tmPending = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001961 status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 break;
1963 }
1964 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1965 msleep(250);
1966 } while (--loop_count);
1967
1968 return status;
1969}
1970
1971/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1972/**
1973 * mptscsih_tm_wait_for_completion - wait for completion of TM task
1974 * @hd: Pointer to MPT host structure.
1975 *
1976 * Returns {SUCCESS,FAILED}.
1977 */
1978static int
1979mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
1980{
1981 unsigned long flags;
1982 int loop_count = 4 * timeout;
1983 int status = FAILED;
1984
1985 do {
1986 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1987 if(hd->tmPending == 0) {
1988 status = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001989 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 break;
1991 }
1992 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1993 msleep_interruptible(250);
1994 } while (--loop_count);
1995
1996 return status;
1997}
1998
1999/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2000/**
2001 * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
2002 * @ioc: Pointer to MPT_ADAPTER structure
2003 * @mf: Pointer to SCSI task mgmt request frame
2004 * @mr: Pointer to SCSI task mgmt reply frame
2005 *
2006 * This routine is called from mptbase.c::mpt_interrupt() at the completion
2007 * of any SCSI task management request.
2008 * This routine is registered with the MPT (base) driver at driver
2009 * load/init time via the mpt_register() API call.
2010 *
2011 * Returns 1 indicating alloc'd request frame ptr should be freed.
2012 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002013int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
2015{
2016 SCSITaskMgmtReply_t *pScsiTmReply;
2017 SCSITaskMgmt_t *pScsiTmReq;
2018 MPT_SCSI_HOST *hd;
2019 unsigned long flags;
2020 u16 iocstatus;
2021 u8 tmType;
2022
2023 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
2024 ioc->name, mf, mr));
2025 if (ioc->sh) {
2026 /* Depending on the thread, a timer is activated for
2027 * the TM request. Delete this timer on completion of TM.
2028 * Decrement count of outstanding TM requests.
2029 */
2030 hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
2031 } else {
2032 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
2033 ioc->name));
2034 return 1;
2035 }
2036
2037 if (mr == NULL) {
2038 dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
2039 ioc->name, mf));
2040 return 1;
2041 } else {
2042 pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
2043 pScsiTmReq = (SCSITaskMgmt_t*)mf;
2044
2045 /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
2046 tmType = pScsiTmReq->TaskType;
2047
2048 dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n",
2049 ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
2050 DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply);
2051
2052 iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2053 dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n",
2054 ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo)));
2055 /* Error? (anything non-zero?) */
2056 if (iocstatus) {
2057
2058 /* clear flags and continue.
2059 */
2060 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
2061 hd->abortSCpnt = NULL;
2062
2063 /* If an internal command is present
2064 * or the TM failed - reload the FW.
2065 * FC FW may respond FAILED to an ABORT
2066 */
2067 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
2068 if ((hd->cmdPtr) ||
2069 (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
2070 if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
2071 printk((KERN_WARNING
2072 " Firmware Reload FAILED!!\n"));
2073 }
2074 }
2075 }
2076 } else {
2077 dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name));
2078
2079 hd->abortSCpnt = NULL;
2080
2081 }
2082 }
2083
2084 spin_lock_irqsave(&ioc->FreeQlock, flags);
2085 hd->tmPending = 0;
2086 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2087 hd->tmState = TM_STATE_NONE;
2088
2089 return 1;
2090}
2091
2092/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2093/*
2094 * This is anyones guess quite frankly.
2095 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002096int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
2098 sector_t capacity, int geom[])
2099{
2100 int heads;
2101 int sectors;
2102 sector_t cylinders;
2103 ulong dummy;
2104
2105 heads = 64;
2106 sectors = 32;
2107
2108 dummy = heads * sectors;
2109 cylinders = capacity;
2110 sector_div(cylinders,dummy);
2111
2112 /*
2113 * Handle extended translation size for logical drives
2114 * > 1Gb
2115 */
2116 if ((ulong)capacity >= 0x200000) {
2117 heads = 255;
2118 sectors = 63;
2119 dummy = heads * sectors;
2120 cylinders = capacity;
2121 sector_div(cylinders,dummy);
2122 }
2123
2124 /* return result */
2125 geom[0] = heads;
2126 geom[1] = sectors;
2127 geom[2] = cylinders;
2128
2129 dprintk((KERN_NOTICE
2130 ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n",
2131 sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors));
2132
2133 return 0;
2134}
2135
2136/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2137/*
2138 * OS entry point to allow host driver to alloc memory
2139 * for each scsi device. Called once per device the bus scan.
2140 * Return non-zero if allocation fails.
2141 * Init memory once per id (not LUN).
2142 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002143int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144mptscsih_slave_alloc(struct scsi_device *device)
2145{
2146 struct Scsi_Host *host = device->host;
2147 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
2148 VirtDevice *vdev;
2149 uint target = device->id;
2150
2151 if (hd == NULL)
2152 return -ENODEV;
2153
2154 if ((vdev = hd->Targets[target]) != NULL)
2155 goto out;
2156
2157 vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL);
2158 if (!vdev) {
2159 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
2160 hd->ioc->name, sizeof(VirtDevice));
2161 return -ENOMEM;
2162 }
2163
2164 memset(vdev, 0, sizeof(VirtDevice));
2165 vdev->tflags = MPT_TARGET_FLAGS_Q_YES;
2166 vdev->ioc_id = hd->ioc->id;
2167 vdev->target_id = device->id;
2168 vdev->bus_id = device->channel;
2169 vdev->raidVolume = 0;
2170 hd->Targets[device->id] = vdev;
2171 if (hd->ioc->bus_type == SCSI) {
2172 if (hd->ioc->spi_data.isRaid & (1 << device->id)) {
2173 vdev->raidVolume = 1;
2174 ddvtprintk((KERN_INFO
2175 "RAID Volume @ id %d\n", device->id));
2176 }
2177 } else {
2178 vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2179 }
2180
2181 out:
2182 vdev->num_luns++;
2183 return 0;
2184}
2185
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002186static int
2187mptscsih_is_raid_volume(MPT_SCSI_HOST *hd, uint id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188{
2189 int i;
2190
2191 if (!hd->ioc->spi_data.isRaid || !hd->ioc->spi_data.pIocPg3)
2192 return 0;
2193
2194 for (i = 0; i < hd->ioc->spi_data.pIocPg3->NumPhysDisks; i++) {
2195 if (id == hd->ioc->spi_data.pIocPg3->PhysDisk[i].PhysDiskID)
2196 return 1;
2197 }
2198
2199 return 0;
2200}
2201
2202/*
2203 * OS entry point to allow for host driver to free allocated memory
2204 * Called if no device present or device being unloaded
2205 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002206void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207mptscsih_slave_destroy(struct scsi_device *device)
2208{
2209 struct Scsi_Host *host = device->host;
2210 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
2211 VirtDevice *vdev;
2212 uint target = device->id;
2213 uint lun = device->lun;
2214
2215 if (hd == NULL)
2216 return;
2217
2218 mptscsih_search_running_cmds(hd, target, lun);
2219
2220 vdev = hd->Targets[target];
2221 vdev->luns[0] &= ~(1 << lun);
2222 if (--vdev->num_luns)
2223 return;
2224
2225 kfree(hd->Targets[target]);
2226 hd->Targets[target] = NULL;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002227
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 if (hd->ioc->bus_type == SCSI) {
2229 if (mptscsih_is_raid_volume(hd, target)) {
2230 hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3;
2231 } else {
2232 hd->ioc->spi_data.dvStatus[target] =
2233 MPT_SCSICFG_NEGOTIATE;
2234
2235 if (!hd->negoNvram) {
2236 hd->ioc->spi_data.dvStatus[target] |=
2237 MPT_SCSICFG_DV_NOT_DONE;
2238 }
2239 }
2240 }
2241}
2242
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002243/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2244/*
2245 * mptscsih_change_queue_depth - This function will set a devices queue depth
2246 * @sdev: per scsi_device pointer
2247 * @qdepth: requested queue depth
2248 *
2249 * Adding support for new 'change_queue_depth' api.
2250*/
2251int
2252mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253{
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002254 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
2255 VirtDevice *pTarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 int max_depth;
2257 int tagged;
2258
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002259 if (hd == NULL)
2260 return 0;
2261 if (!(pTarget = hd->Targets[sdev->id]))
2262 return 0;
2263
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 if (hd->ioc->bus_type == SCSI) {
2265 if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) {
2266 if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES))
2267 max_depth = 1;
2268 else if (((pTarget->inq_data[0] & 0x1f) == 0x00) &&
2269 (pTarget->minSyncFactor <= MPT_ULTRA160 ))
2270 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2271 else
2272 max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
2273 } else {
2274 /* error case - No Inq. Data */
2275 max_depth = 1;
2276 }
2277 } else
2278 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2279
2280 if (qdepth > max_depth)
2281 qdepth = max_depth;
2282 if (qdepth == 1)
2283 tagged = 0;
2284 else
2285 tagged = MSG_SIMPLE_TAG;
2286
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002287 scsi_adjust_queue_depth(sdev, tagged, qdepth);
2288 return sdev->queue_depth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289}
2290
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291/*
2292 * OS entry point to adjust the queue_depths on a per-device basis.
2293 * Called once per device the bus scan. Use it to force the queue_depth
2294 * member to 1 if a device does not support Q tags.
2295 * Return non-zero if fails.
2296 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002297int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298mptscsih_slave_configure(struct scsi_device *device)
2299{
2300 struct Scsi_Host *sh = device->host;
2301 VirtDevice *pTarget;
2302 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sh->hostdata;
2303
2304 if ((hd == NULL) || (hd->Targets == NULL)) {
2305 return 0;
2306 }
2307
2308 dsprintk((MYIOC_s_INFO_FMT
2309 "device @ %p, id=%d, LUN=%d, channel=%d\n",
2310 hd->ioc->name, device, device->id, device->lun, device->channel));
2311 dsprintk((MYIOC_s_INFO_FMT
2312 "sdtr %d wdtr %d ppr %d inq length=%d\n",
2313 hd->ioc->name, device->sdtr, device->wdtr,
2314 device->ppr, device->inquiry_len));
2315
2316 if (device->id > sh->max_id) {
2317 /* error case, should never happen */
2318 scsi_adjust_queue_depth(device, 0, 1);
2319 goto slave_configure_exit;
2320 }
2321
2322 pTarget = hd->Targets[device->id];
2323
2324 if (pTarget == NULL) {
2325 /* Driver doesn't know about this device.
2326 * Kernel may generate a "Dummy Lun 0" which
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002327 * may become a real Lun if a
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 * "scsi add-single-device" command is executed
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002329 * while the driver is active (hot-plug a
2330 * device). LSI Raid controllers need
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 * queue_depth set to DEV_HIGH for this reason.
2332 */
2333 scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
2334 MPT_SCSI_CMD_PER_DEV_HIGH);
2335 goto slave_configure_exit;
2336 }
2337
2338 mptscsih_initTarget(hd, device->channel, device->id, device->lun,
2339 device->inquiry, device->inquiry_len );
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002340 mptscsih_change_queue_depth(device, MPT_SCSI_CMD_PER_DEV_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341
2342 dsprintk((MYIOC_s_INFO_FMT
2343 "Queue depth=%d, tflags=%x\n",
2344 hd->ioc->name, device->queue_depth, pTarget->tflags));
2345
2346 dsprintk((MYIOC_s_INFO_FMT
2347 "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n",
2348 hd->ioc->name, pTarget->negoFlags, pTarget->maxOffset, pTarget->minSyncFactor));
2349
2350slave_configure_exit:
2351
2352 dsprintk((MYIOC_s_INFO_FMT
2353 "tagged %d, simple %d, ordered %d\n",
2354 hd->ioc->name,device->tagged_supported, device->simple_tags,
2355 device->ordered_tags));
2356
2357 return 0;
2358}
2359
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2361/*
2362 * Private routines...
2363 */
2364
2365/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2366/* Utility function to copy sense data from the scsi_cmnd buffer
2367 * to the FC and SCSI target structures.
2368 *
2369 */
2370static void
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002371mptscsih_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 -07002372{
2373 VirtDevice *target;
2374 SCSIIORequest_t *pReq;
2375 u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
2376 int index;
2377
2378 /* Get target structure
2379 */
2380 pReq = (SCSIIORequest_t *) mf;
2381 index = (int) pReq->TargetID;
2382 target = hd->Targets[index];
2383
2384 if (sense_count) {
2385 u8 *sense_data;
2386 int req_index;
2387
2388 /* Copy the sense received into the scsi command block. */
2389 req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2390 sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
2391 memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
2392
2393 /* Log SMART data (asc = 0x5D, non-IM case only) if required.
2394 */
2395 if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
2396 if ((sense_data[12] == 0x5D) && (target->raidVolume == 0)) {
2397 int idx;
2398 MPT_ADAPTER *ioc = hd->ioc;
2399
2400 idx = ioc->eventContext % ioc->eventLogSize;
2401 ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
2402 ioc->events[idx].eventContext = ioc->eventContext;
2403
2404 ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
2405 (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
2406 (pReq->Bus << 8) || pReq->TargetID;
2407
2408 ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
2409
2410 ioc->eventContext++;
2411 }
2412 }
2413 } else {
2414 dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
2415 hd->ioc->name));
2416 }
2417}
2418
2419static u32
2420SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
2421{
2422 MPT_SCSI_HOST *hd;
2423 int i;
2424
2425 hd = (MPT_SCSI_HOST *) sc->device->host->hostdata;
2426
2427 for (i = 0; i < hd->ioc->req_depth; i++) {
2428 if (hd->ScsiLookup[i] == sc) {
2429 return i;
2430 }
2431 }
2432
2433 return -1;
2434}
2435
2436/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002437int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
2439{
2440 MPT_SCSI_HOST *hd;
2441 unsigned long flags;
2442
2443 dtmprintk((KERN_WARNING MYNAM
2444 ": IOC %s_reset routed to SCSI host driver!\n",
2445 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
2446 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
2447
2448 /* If a FW reload request arrives after base installed but
2449 * before all scsi hosts have been attached, then an alt_ioc
2450 * may have a NULL sh pointer.
2451 */
2452 if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
2453 return 0;
2454 else
2455 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2456
2457 if (reset_phase == MPT_IOC_SETUP_RESET) {
2458 dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
2459
2460 /* Clean Up:
2461 * 1. Set Hard Reset Pending Flag
2462 * All new commands go to doneQ
2463 */
2464 hd->resetPending = 1;
2465
2466 } else if (reset_phase == MPT_IOC_PRE_RESET) {
2467 dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
2468
2469 /* 2. Flush running commands
2470 * Clean ScsiLookup (and associated memory)
2471 * AND clean mytaskQ
2472 */
2473
2474 /* 2b. Reply to OS all known outstanding I/O commands.
2475 */
2476 mptscsih_flush_running_cmds(hd);
2477
2478 /* 2c. If there was an internal command that
2479 * has not completed, configuration or io request,
2480 * free these resources.
2481 */
2482 if (hd->cmdPtr) {
2483 del_timer(&hd->timer);
2484 mpt_free_msg_frame(ioc, hd->cmdPtr);
2485 }
2486
2487 dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
2488
2489 } else {
2490 dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
2491
2492 /* Once a FW reload begins, all new OS commands are
2493 * redirected to the doneQ w/ a reset status.
2494 * Init all control structures.
2495 */
2496
2497 /* ScsiLookup initialization
2498 */
2499 {
2500 int ii;
2501 for (ii=0; ii < hd->ioc->req_depth; ii++)
2502 hd->ScsiLookup[ii] = NULL;
2503 }
2504
2505 /* 2. Chain Buffer initialization
2506 */
2507
2508 /* 4. Renegotiate to all devices, if SCSI
2509 */
2510 if (ioc->bus_type == SCSI) {
2511 dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n"));
2512 mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM);
2513 }
2514
2515 /* 5. Enable new commands to be posted
2516 */
2517 spin_lock_irqsave(&ioc->FreeQlock, flags);
2518 hd->tmPending = 0;
2519 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2520 hd->resetPending = 0;
2521 hd->tmState = TM_STATE_NONE;
2522
2523 /* 6. If there was an internal command,
2524 * wake this process up.
2525 */
2526 if (hd->cmdPtr) {
2527 /*
2528 * Wake up the original calling thread
2529 */
2530 hd->pLocal = &hd->localReply;
2531 hd->pLocal->completion = MPT_SCANDV_DID_RESET;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002532 hd->scandv_wait_done = 1;
2533 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 hd->cmdPtr = NULL;
2535 }
2536
2537 /* 7. Set flag to force DV and re-read IOC Page 3
2538 */
2539 if (ioc->bus_type == SCSI) {
2540 ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
2541 ddvtprintk(("Set reload IOC Pg3 Flag\n"));
2542 }
2543
2544 dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
2545
2546 }
2547
2548 return 1; /* currently means nothing really */
2549}
2550
2551/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002552int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
2554{
2555 MPT_SCSI_HOST *hd;
2556 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
2557
2558 devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
2559 ioc->name, event));
2560
2561 switch (event) {
2562 case MPI_EVENT_UNIT_ATTENTION: /* 03 */
2563 /* FIXME! */
2564 break;
2565 case MPI_EVENT_IOC_BUS_RESET: /* 04 */
2566 case MPI_EVENT_EXT_BUS_RESET: /* 05 */
2567 hd = NULL;
2568 if (ioc->sh) {
2569 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2570 if (hd && (ioc->bus_type == SCSI) && (hd->soft_resets < -1))
2571 hd->soft_resets++;
2572 }
2573 break;
2574 case MPI_EVENT_LOGOUT: /* 09 */
2575 /* FIXME! */
2576 break;
2577
2578 /*
2579 * CHECKME! Don't think we need to do
2580 * anything for these, but...
2581 */
2582 case MPI_EVENT_RESCAN: /* 06 */
2583 case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
2584 case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
2585 /*
2586 * CHECKME! Falling thru...
2587 */
2588 break;
2589
2590 case MPI_EVENT_INTEGRATED_RAID: /* 0B */
2591#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
2592 /* negoNvram set to 0 if DV enabled and to USE_NVRAM if
2593 * if DV disabled. Need to check for target mode.
2594 */
2595 hd = NULL;
2596 if (ioc->sh)
2597 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2598
2599 if (hd && (ioc->bus_type == SCSI) && (hd->negoNvram == 0)) {
2600 ScsiCfgData *pSpi;
2601 Ioc3PhysDisk_t *pPDisk;
2602 int numPDisk;
2603 u8 reason;
2604 u8 physDiskNum;
2605
2606 reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16;
2607 if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) {
2608 /* New or replaced disk.
2609 * Set DV flag and schedule DV.
2610 */
2611 pSpi = &ioc->spi_data;
2612 physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24;
2613 ddvtprintk(("DV requested for phys disk id %d\n", physDiskNum));
2614 if (pSpi->pIocPg3) {
2615 pPDisk = pSpi->pIocPg3->PhysDisk;
2616 numPDisk =pSpi->pIocPg3->NumPhysDisks;
2617
2618 while (numPDisk) {
2619 if (physDiskNum == pPDisk->PhysDiskNum) {
2620 pSpi->dvStatus[pPDisk->PhysDiskID] = (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE);
2621 pSpi->forceDv = MPT_SCSICFG_NEED_DV;
2622 ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
2623 break;
2624 }
2625 pPDisk++;
2626 numPDisk--;
2627 }
2628
2629 if (numPDisk == 0) {
2630 /* The physical disk that needs DV was not found
2631 * in the stored IOC Page 3. The driver must reload
2632 * this page. DV routine will set the NEED_DV flag for
2633 * all phys disks that have DV_NOT_DONE set.
2634 */
2635 pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
2636 ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n", physDiskNum));
2637 }
2638 }
2639 }
2640 }
2641#endif
2642
2643#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
2644 printk("Raid Event RF: ");
2645 {
2646 u32 *m = (u32 *)pEvReply;
2647 int ii;
2648 int n = (int)pEvReply->MsgLength;
2649 for (ii=6; ii < n; ii++)
2650 printk(" %08x", le32_to_cpu(m[ii]));
2651 printk("\n");
2652 }
2653#endif
2654 break;
2655
2656 case MPI_EVENT_NONE: /* 00 */
2657 case MPI_EVENT_LOG_DATA: /* 01 */
2658 case MPI_EVENT_STATE_CHANGE: /* 02 */
2659 case MPI_EVENT_EVENT_CHANGE: /* 0A */
2660 default:
2661 dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
2662 break;
2663 }
2664
2665 return 1; /* currently means nothing really */
2666}
2667
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2669/*
2670 * mptscsih_initTarget - Target, LUN alloc/free functionality.
2671 * @hd: Pointer to MPT_SCSI_HOST structure
2672 * @bus_id: Bus number (?)
2673 * @target_id: SCSI target id
2674 * @lun: SCSI LUN id
2675 * @data: Pointer to data
2676 * @dlen: Number of INQUIRY bytes
2677 *
2678 * NOTE: It's only SAFE to call this routine if data points to
2679 * sane & valid STANDARD INQUIRY data!
2680 *
2681 * Allocate and initialize memory for this target.
2682 * Save inquiry data.
2683 *
2684 */
2685static void
2686mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen)
2687{
2688 int indexed_lun, lun_index;
2689 VirtDevice *vdev;
2690 ScsiCfgData *pSpi;
2691 char data_56;
2692
2693 dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n",
2694 hd->ioc->name, bus_id, target_id, lun, hd));
2695
2696 /*
2697 * If the peripheral qualifier filter is enabled then if the target reports a 0x1
2698 * (i.e. The targer is capable of supporting the specified peripheral device type
2699 * on this logical unit; however, the physical device is not currently connected
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002700 * to this logical unit) it will be converted to a 0x3 (i.e. The target is not
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 * capable of supporting a physical device on this logical unit). This is to work
2702 * around a bug in th emid-layer in some distributions in which the mid-layer will
2703 * continue to try to communicate to the LUN and evntually create a dummy LUN.
2704 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002705 if (hd->mpt_pq_filter && dlen && (data[0] & 0xE0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 data[0] |= 0x40;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002707
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 /* Is LUN supported? If so, upper 2 bits will be 0
2709 * in first byte of inquiry data.
2710 */
2711 if (data[0] & 0xe0)
2712 return;
2713
2714 if ((vdev = hd->Targets[target_id]) == NULL) {
2715 return;
2716 }
2717
2718 lun_index = (lun >> 5); /* 32 luns per lun_index */
2719 indexed_lun = (lun % 32);
2720 vdev->luns[lun_index] |= (1 << indexed_lun);
2721
2722 if (hd->ioc->bus_type == SCSI) {
2723 if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) {
2724 /* Treat all Processors as SAF-TE if
2725 * command line option is set */
2726 vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2727 mptscsih_writeIOCPage4(hd, target_id, bus_id);
2728 }else if ((data[0] == TYPE_PROCESSOR) &&
2729 !(vdev->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) {
2730 if ( dlen > 49 ) {
2731 vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2732 if ( data[44] == 'S' &&
2733 data[45] == 'A' &&
2734 data[46] == 'F' &&
2735 data[47] == '-' &&
2736 data[48] == 'T' &&
2737 data[49] == 'E' ) {
2738 vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2739 mptscsih_writeIOCPage4(hd, target_id, bus_id);
2740 }
2741 }
2742 }
2743 if (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) {
2744 if ( dlen > 8 ) {
2745 memcpy (vdev->inq_data, data, 8);
2746 } else {
2747 memcpy (vdev->inq_data, data, dlen);
2748 }
2749
2750 /* If have not done DV, set the DV flag.
2751 */
2752 pSpi = &hd->ioc->spi_data;
2753 if ((data[0] == TYPE_TAPE) || (data[0] == TYPE_PROCESSOR)) {
2754 if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE)
2755 pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV;
2756 }
2757
2758 vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2759
2760
2761 data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */
2762 if (dlen > 56) {
2763 if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2764 /* Update the target capabilities
2765 */
2766 data_56 = data[56];
2767 vdev->tflags |= MPT_TARGET_FLAGS_VALID_56;
2768 }
2769 }
2770 mptscsih_setTargetNegoParms(hd, vdev, data_56);
2771 } else {
2772 /* Initial Inquiry may not request enough data bytes to
2773 * obtain byte 57. DV will; if target doesn't return
2774 * at least 57 bytes, data[56] will be zero. */
2775 if (dlen > 56) {
2776 if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2777 /* Update the target capabilities
2778 */
2779 data_56 = data[56];
2780 vdev->tflags |= MPT_TARGET_FLAGS_VALID_56;
2781 mptscsih_setTargetNegoParms(hd, vdev, data_56);
2782 }
2783 }
2784 }
2785 }
2786}
2787
2788/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2789/*
2790 * Update the target negotiation parameters based on the
2791 * the Inquiry data, adapter capabilities, and NVRAM settings.
2792 *
2793 */
2794static void
2795mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56)
2796{
2797 ScsiCfgData *pspi_data = &hd->ioc->spi_data;
2798 int id = (int) target->target_id;
2799 int nvram;
2800 VirtDevice *vdev;
2801 int ii;
2802 u8 width = MPT_NARROW;
2803 u8 factor = MPT_ASYNC;
2804 u8 offset = 0;
2805 u8 version, nfactor;
2806 u8 noQas = 1;
2807
2808 target->negoFlags = pspi_data->noQas;
2809
2810 /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine
2811 * support. If available, default QAS to off and allow enabling.
2812 * If not available, default QAS to on, turn off for non-disks.
2813 */
2814
2815 /* Set flags based on Inquiry data
2816 */
2817 version = target->inq_data[2] & 0x07;
2818 if (version < 2) {
2819 width = 0;
2820 factor = MPT_ULTRA2;
2821 offset = pspi_data->maxSyncOffset;
2822 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2823 } else {
2824 if (target->inq_data[7] & 0x20) {
2825 width = 1;
2826 }
2827
2828 if (target->inq_data[7] & 0x10) {
2829 factor = pspi_data->minSyncFactor;
2830 if (target->tflags & MPT_TARGET_FLAGS_VALID_56) {
2831 /* bits 2 & 3 show Clocking support */
2832 if ((byte56 & 0x0C) == 0)
2833 factor = MPT_ULTRA2;
2834 else {
2835 if ((byte56 & 0x03) == 0)
2836 factor = MPT_ULTRA160;
2837 else {
2838 factor = MPT_ULTRA320;
2839 if (byte56 & 0x02)
2840 {
2841 ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id));
2842 noQas = 0;
2843 }
2844 if (target->inq_data[0] == TYPE_TAPE) {
2845 if (byte56 & 0x01)
2846 target->negoFlags |= MPT_TAPE_NEGO_IDP;
2847 }
2848 }
2849 }
2850 } else {
2851 ddvtprintk((KERN_INFO "Enabling QAS on id=%d due to ~TARGET_FLAGS_VALID_56!\n", id));
2852 noQas = 0;
2853 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002854
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 offset = pspi_data->maxSyncOffset;
2856
2857 /* If RAID, never disable QAS
2858 * else if non RAID, do not disable
2859 * QAS if bit 1 is set
2860 * bit 1 QAS support, non-raid only
2861 * bit 0 IU support
2862 */
2863 if (target->raidVolume == 1) {
2864 noQas = 0;
2865 }
2866 } else {
2867 factor = MPT_ASYNC;
2868 offset = 0;
2869 }
2870 }
2871
2872 if ( (target->inq_data[7] & 0x02) == 0) {
2873 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2874 }
2875
2876 /* Update tflags based on NVRAM settings. (SCSI only)
2877 */
2878 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
2879 nvram = pspi_data->nvram[id];
2880 nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
2881
2882 if (width)
2883 width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
2884
2885 if (offset > 0) {
2886 /* Ensure factor is set to the
2887 * maximum of: adapter, nvram, inquiry
2888 */
2889 if (nfactor) {
2890 if (nfactor < pspi_data->minSyncFactor )
2891 nfactor = pspi_data->minSyncFactor;
2892
2893 factor = max(factor, nfactor);
2894 if (factor == MPT_ASYNC)
2895 offset = 0;
2896 } else {
2897 offset = 0;
2898 factor = MPT_ASYNC;
2899 }
2900 } else {
2901 factor = MPT_ASYNC;
2902 }
2903 }
2904
2905 /* Make sure data is consistent
2906 */
2907 if ((!width) && (factor < MPT_ULTRA2)) {
2908 factor = MPT_ULTRA2;
2909 }
2910
2911 /* Save the data to the target structure.
2912 */
2913 target->minSyncFactor = factor;
2914 target->maxOffset = offset;
2915 target->maxWidth = width;
2916
2917 target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
2918
2919 /* Disable unused features.
2920 */
2921 if (!width)
2922 target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
2923
2924 if (!offset)
2925 target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
2926
2927 if ( factor > MPT_ULTRA320 )
2928 noQas = 0;
2929
2930 /* GEM, processor WORKAROUND
2931 */
2932 if ((target->inq_data[0] == TYPE_PROCESSOR) || (target->inq_data[0] > 0x08)) {
2933 target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC);
2934 pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO;
2935 } else {
2936 if (noQas && (pspi_data->noQas == 0)) {
2937 pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS;
2938 target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2939
2940 /* Disable QAS in a mixed configuration case
2941 */
2942
2943 ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id));
2944 for (ii = 0; ii < id; ii++) {
2945 if ( (vdev = hd->Targets[ii]) ) {
2946 vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2947 mptscsih_writeSDP1(hd, 0, ii, vdev->negoFlags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002948 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 }
2950 }
2951 }
2952
2953 /* Write SDP1 on this I/O to this target */
2954 if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) {
2955 ddvtprintk((KERN_INFO "MPT_SCSICFG_NEGOTIATE on id=%d!\n", id));
2956 mptscsih_writeSDP1(hd, 0, id, hd->negoNvram);
2957 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE;
2958 } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) {
2959 ddvtprintk((KERN_INFO "MPT_SCSICFG_BLK_NEGO on id=%d!\n", id));
2960 mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO);
2961 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO;
2962 }
2963}
2964
2965/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2966/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
2967 * Else set the NEED_DV flag after Read Capacity Issued (disks)
2968 * or Mode Sense (cdroms).
2969 *
2970 * Tapes, initTarget will set this flag on completion of Inquiry command.
2971 * Called only if DV_NOT_DONE flag is set
2972 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002973static void
2974mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975{
2976 u8 cmd;
2977 ScsiCfgData *pSpi;
2978
2979 ddvtprintk((" set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n",
2980 pReq->TargetID, pReq->LUN[1], hd->negoNvram, pReq->CDB[0]));
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002981
Linus Torvalds1da177e2005-04-16 15:20:36 -07002982 if ((pReq->LUN[1] != 0) || (hd->negoNvram != 0))
2983 return;
2984
2985 cmd = pReq->CDB[0];
2986
2987 if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) {
2988 pSpi = &hd->ioc->spi_data;
2989 if ((pSpi->isRaid & (1 << pReq->TargetID)) && pSpi->pIocPg3) {
2990 /* Set NEED_DV for all hidden disks
2991 */
2992 Ioc3PhysDisk_t *pPDisk = pSpi->pIocPg3->PhysDisk;
2993 int numPDisk = pSpi->pIocPg3->NumPhysDisks;
2994
2995 while (numPDisk) {
2996 pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
2997 ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
2998 pPDisk++;
2999 numPDisk--;
3000 }
3001 }
3002 pSpi->dvStatus[pReq->TargetID] |= MPT_SCSICFG_NEED_DV;
3003 ddvtprintk(("NEED_DV set for visible disk id %d\n", pReq->TargetID));
3004 }
3005}
3006
3007/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3008/*
3009 * If no Target, bus reset on 1st I/O. Set the flag to
3010 * prevent any future negotiations to this device.
3011 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003012static void
3013mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003014{
3015
3016 if ((hd->Targets) && (hd->Targets[target_id] == NULL))
3017 hd->ioc->spi_data.dvStatus[target_id] |= MPT_SCSICFG_BLK_NEGO;
3018
3019 return;
3020}
3021
3022/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3023/*
3024 * SCSI Config Page functionality ...
3025 */
3026/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3027/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags
3028 * based on width, factor and offset parameters.
3029 * @width: bus width
3030 * @factor: sync factor
3031 * @offset: sync offset
3032 * @requestedPtr: pointer to requested values (updated)
3033 * @configurationPtr: pointer to configuration values (updated)
3034 * @flags: flags to block WDTR or SDTR negotiation
3035 *
3036 * Return: None.
3037 *
3038 * Remark: Called by writeSDP1 and _dv_params
3039 */
3040static void
3041mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags)
3042{
3043 u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE;
3044 u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC;
3045
3046 *configurationPtr = 0;
3047 *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0;
3048 *requestedPtr |= (offset << 16) | (factor << 8);
3049
3050 if (width && offset && !nowide && !nosync) {
3051 if (factor < MPT_ULTRA160) {
3052 *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT);
3053 if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0)
3054 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS;
3055 if (flags & MPT_TAPE_NEGO_IDP)
3056 *requestedPtr |= 0x08000000;
3057 } else if (factor < MPT_ULTRA2) {
3058 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT;
3059 }
3060 }
3061
3062 if (nowide)
3063 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED;
3064
3065 if (nosync)
3066 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED;
3067
3068 return;
3069}
3070
3071/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3072/* mptscsih_writeSDP1 - write SCSI Device Page 1
3073 * @hd: Pointer to a SCSI Host Strucutre
3074 * @portnum: IOC port number
3075 * @target_id: writeSDP1 for single ID
3076 * @flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO
3077 *
3078 * Return: -EFAULT if read of config page header fails
3079 * or 0 if success.
3080 *
3081 * Remark: If a target has been found, the settings from the
3082 * target structure are used, else the device is set
3083 * to async/narrow.
3084 *
3085 * Remark: Called during init and after a FW reload.
3086 * Remark: We do not wait for a return, write pages sequentially.
3087 */
3088static int
3089mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags)
3090{
3091 MPT_ADAPTER *ioc = hd->ioc;
3092 Config_t *pReq;
3093 SCSIDevicePage1_t *pData;
3094 VirtDevice *pTarget;
3095 MPT_FRAME_HDR *mf;
3096 dma_addr_t dataDma;
3097 u16 req_idx;
3098 u32 frameOffset;
3099 u32 requested, configuration, flagsLength;
3100 int ii, nvram;
3101 int id = 0, maxid = 0;
3102 u8 width;
3103 u8 factor;
3104 u8 offset;
3105 u8 bus = 0;
3106 u8 negoFlags;
3107 u8 maxwidth, maxoffset, maxfactor;
3108
3109 if (ioc->spi_data.sdp1length == 0)
3110 return 0;
3111
3112 if (flags & MPT_SCSICFG_ALL_IDS) {
3113 id = 0;
3114 maxid = ioc->sh->max_id - 1;
3115 } else if (ioc->sh) {
3116 id = target_id;
3117 maxid = min_t(int, id, ioc->sh->max_id - 1);
3118 }
3119
3120 for (; id <= maxid; id++) {
3121
3122 if (id == ioc->pfacts[portnum].PortSCSIID)
3123 continue;
3124
3125 /* Use NVRAM to get adapter and target maximums
3126 * Data over-riden by target structure information, if present
3127 */
3128 maxwidth = ioc->spi_data.maxBusWidth;
3129 maxoffset = ioc->spi_data.maxSyncOffset;
3130 maxfactor = ioc->spi_data.minSyncFactor;
3131 if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3132 nvram = ioc->spi_data.nvram[id];
3133
3134 if (maxwidth)
3135 maxwidth = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
3136
3137 if (maxoffset > 0) {
3138 maxfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
3139 if (maxfactor == 0) {
3140 /* Key for async */
3141 maxfactor = MPT_ASYNC;
3142 maxoffset = 0;
3143 } else if (maxfactor < ioc->spi_data.minSyncFactor) {
3144 maxfactor = ioc->spi_data.minSyncFactor;
3145 }
3146 } else
3147 maxfactor = MPT_ASYNC;
3148 }
3149
3150 /* Set the negotiation flags.
3151 */
3152 negoFlags = ioc->spi_data.noQas;
3153 if (!maxwidth)
3154 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
3155
3156 if (!maxoffset)
3157 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
3158
3159 if (flags & MPT_SCSICFG_USE_NVRAM) {
3160 width = maxwidth;
3161 factor = maxfactor;
3162 offset = maxoffset;
3163 } else {
3164 width = 0;
3165 factor = MPT_ASYNC;
3166 offset = 0;
3167 //negoFlags = 0;
3168 //negoFlags = MPT_TARGET_NO_NEGO_SYNC;
3169 }
3170
3171 /* If id is not a raid volume, get the updated
3172 * transmission settings from the target structure.
3173 */
3174 if (hd->Targets && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) {
3175 width = pTarget->maxWidth;
3176 factor = pTarget->minSyncFactor;
3177 offset = pTarget->maxOffset;
3178 negoFlags = pTarget->negoFlags;
3179 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003180
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3182 /* Force to async and narrow if DV has not been executed
3183 * for this ID
3184 */
3185 if ((hd->ioc->spi_data.dvStatus[id] & MPT_SCSICFG_DV_NOT_DONE) != 0) {
3186 width = 0;
3187 factor = MPT_ASYNC;
3188 offset = 0;
3189 }
3190#endif
3191
3192 if (flags & MPT_SCSICFG_BLK_NEGO)
3193 negoFlags = MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC;
3194
3195 mptscsih_setDevicePage1Flags(width, factor, offset,
3196 &requested, &configuration, negoFlags);
3197 dnegoprintk(("writeSDP1: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
3198 target_id, width, factor, offset, negoFlags, requested, configuration));
3199
3200 /* Get a MF for this command.
3201 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003202 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003203 dfailprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n",
3204 ioc->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003205 return -EAGAIN;
3206 }
3207
3208 ddvprintk((MYIOC_s_INFO_FMT "WriteSDP1 (mf=%p, id=%d, req=0x%x, cfg=0x%x)\n",
3209 hd->ioc->name, mf, id, requested, configuration));
3210
3211
3212 /* Set the request and the data pointers.
3213 * Request takes: 36 bytes (32 bit SGE)
3214 * SCSI Device Page 1 requires 16 bytes
3215 * 40 + 16 <= size of SCSI IO Request = 56 bytes
3216 * and MF size >= 64 bytes.
3217 * Place data at end of MF.
3218 */
3219 pReq = (Config_t *)mf;
3220
3221 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3222 frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t);
3223
3224 pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset);
3225 dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset;
3226
3227 /* Complete the request frame (same for all requests).
3228 */
3229 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3230 pReq->Reserved = 0;
3231 pReq->ChainOffset = 0;
3232 pReq->Function = MPI_FUNCTION_CONFIG;
3233 pReq->ExtPageLength = 0;
3234 pReq->ExtPageType = 0;
3235 pReq->MsgFlags = 0;
3236 for (ii=0; ii < 8; ii++) {
3237 pReq->Reserved2[ii] = 0;
3238 }
3239 pReq->Header.PageVersion = ioc->spi_data.sdp1version;
3240 pReq->Header.PageLength = ioc->spi_data.sdp1length;
3241 pReq->Header.PageNumber = 1;
3242 pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
3243 pReq->PageAddress = cpu_to_le32(id | (bus << 8 ));
3244
3245 /* Add a SGE to the config request.
3246 */
3247 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4;
3248
3249 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3250
3251 /* Set up the common data portion
3252 */
3253 pData->Header.PageVersion = pReq->Header.PageVersion;
3254 pData->Header.PageLength = pReq->Header.PageLength;
3255 pData->Header.PageNumber = pReq->Header.PageNumber;
3256 pData->Header.PageType = pReq->Header.PageType;
3257 pData->RequestedParameters = cpu_to_le32(requested);
3258 pData->Reserved = 0;
3259 pData->Configuration = cpu_to_le32(configuration);
3260
3261 dprintk((MYIOC_s_INFO_FMT
3262 "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n",
3263 ioc->name, id, (id | (bus<<8)),
3264 requested, configuration));
3265
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003266 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003267 }
3268
3269 return 0;
3270}
3271
3272/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3273/* mptscsih_writeIOCPage4 - write IOC Page 4
3274 * @hd: Pointer to a SCSI Host Structure
3275 * @target_id: write IOC Page4 for this ID & Bus
3276 *
3277 * Return: -EAGAIN if unable to obtain a Message Frame
3278 * or 0 if success.
3279 *
3280 * Remark: We do not wait for a return, write pages sequentially.
3281 */
3282static int
3283mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus)
3284{
3285 MPT_ADAPTER *ioc = hd->ioc;
3286 Config_t *pReq;
3287 IOCPage4_t *IOCPage4Ptr;
3288 MPT_FRAME_HDR *mf;
3289 dma_addr_t dataDma;
3290 u16 req_idx;
3291 u32 frameOffset;
3292 u32 flagsLength;
3293 int ii;
3294
3295 /* Get a MF for this command.
3296 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003297 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003298 dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003299 ioc->name));
3300 return -EAGAIN;
3301 }
3302
3303 /* Set the request and the data pointers.
3304 * Place data at end of MF.
3305 */
3306 pReq = (Config_t *)mf;
3307
3308 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3309 frameOffset = ioc->req_sz - sizeof(IOCPage4_t);
3310
3311 /* Complete the request frame (same for all requests).
3312 */
3313 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3314 pReq->Reserved = 0;
3315 pReq->ChainOffset = 0;
3316 pReq->Function = MPI_FUNCTION_CONFIG;
3317 pReq->ExtPageLength = 0;
3318 pReq->ExtPageType = 0;
3319 pReq->MsgFlags = 0;
3320 for (ii=0; ii < 8; ii++) {
3321 pReq->Reserved2[ii] = 0;
3322 }
3323
3324 IOCPage4Ptr = ioc->spi_data.pIocPg4;
3325 dataDma = ioc->spi_data.IocPg4_dma;
3326 ii = IOCPage4Ptr->ActiveSEP++;
3327 IOCPage4Ptr->SEP[ii].SEPTargetID = target_id;
3328 IOCPage4Ptr->SEP[ii].SEPBus = bus;
3329 pReq->Header = IOCPage4Ptr->Header;
3330 pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 ));
3331
3332 /* Add a SGE to the config request.
3333 */
3334 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
3335 (IOCPage4Ptr->Header.PageLength + ii) * 4;
3336
3337 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3338
3339 dinitprintk((MYIOC_s_INFO_FMT
3340 "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
3341 ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus));
3342
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003343 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344
3345 return 0;
3346}
3347
3348/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3349/*
3350 * Bus Scan and Domain Validation functionality ...
3351 */
3352
3353/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3354/*
3355 * mptscsih_scandv_complete - Scan and DV callback routine registered
3356 * to Fustion MPT (base) driver.
3357 *
3358 * @ioc: Pointer to MPT_ADAPTER structure
3359 * @mf: Pointer to original MPT request frame
3360 * @mr: Pointer to MPT reply frame (NULL if TurboReply)
3361 *
3362 * This routine is called from mpt.c::mpt_interrupt() at the completion
3363 * of any SCSI IO request.
3364 * This routine is registered with the Fusion MPT (base) driver at driver
3365 * load/init time via the mpt_register() API call.
3366 *
3367 * Returns 1 indicating alloc'd request frame ptr should be freed.
3368 *
3369 * Remark: Sets a completion code and (possibly) saves sense data
3370 * in the IOC member localReply structure.
3371 * Used ONLY for DV and other internal commands.
3372 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003373int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003374mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
3375{
3376 MPT_SCSI_HOST *hd;
3377 SCSIIORequest_t *pReq;
3378 int completionCode;
3379 u16 req_idx;
3380
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003381 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
3382
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383 if ((mf == NULL) ||
3384 (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
3385 printk(MYIOC_s_ERR_FMT
3386 "ScanDvComplete, %s req frame ptr! (=%p)\n",
3387 ioc->name, mf?"BAD":"NULL", (void *) mf);
3388 goto wakeup;
3389 }
3390
Linus Torvalds1da177e2005-04-16 15:20:36 -07003391 del_timer(&hd->timer);
3392 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3393 hd->ScsiLookup[req_idx] = NULL;
3394 pReq = (SCSIIORequest_t *) mf;
3395
3396 if (mf != hd->cmdPtr) {
3397 printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
3398 hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
3399 }
3400 hd->cmdPtr = NULL;
3401
3402 ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
3403 hd->ioc->name, mf, mr, req_idx));
3404
3405 hd->pLocal = &hd->localReply;
3406 hd->pLocal->scsiStatus = 0;
3407
3408 /* If target struct exists, clear sense valid flag.
3409 */
3410 if (mr == NULL) {
3411 completionCode = MPT_SCANDV_GOOD;
3412 } else {
3413 SCSIIOReply_t *pReply;
3414 u16 status;
3415 u8 scsi_status;
3416
3417 pReply = (SCSIIOReply_t *) mr;
3418
3419 status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
3420 scsi_status = pReply->SCSIStatus;
3421
3422 ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
3423 status, pReply->SCSIState, scsi_status,
3424 le32_to_cpu(pReply->IOCLogInfo)));
3425
3426 switch(status) {
3427
3428 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
3429 completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
3430 break;
3431
3432 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
3433 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
3434 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
3435 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
3436 completionCode = MPT_SCANDV_DID_RESET;
3437 break;
3438
3439 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
3440 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
3441 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
3442 if (pReply->Function == MPI_FUNCTION_CONFIG) {
3443 ConfigReply_t *pr = (ConfigReply_t *)mr;
3444 completionCode = MPT_SCANDV_GOOD;
3445 hd->pLocal->header.PageVersion = pr->Header.PageVersion;
3446 hd->pLocal->header.PageLength = pr->Header.PageLength;
3447 hd->pLocal->header.PageNumber = pr->Header.PageNumber;
3448 hd->pLocal->header.PageType = pr->Header.PageType;
3449
3450 } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
3451 /* If the RAID Volume request is successful,
3452 * return GOOD, else indicate that
3453 * some type of error occurred.
3454 */
3455 MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
Christoph Hellwig637fa992005-08-18 16:25:44 +02003456 if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003457 completionCode = MPT_SCANDV_GOOD;
3458 else
3459 completionCode = MPT_SCANDV_SOME_ERROR;
3460
3461 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
3462 u8 *sense_data;
3463 int sz;
3464
3465 /* save sense data in global structure
3466 */
3467 completionCode = MPT_SCANDV_SENSE;
3468 hd->pLocal->scsiStatus = scsi_status;
3469 sense_data = ((u8 *)hd->ioc->sense_buf_pool +
3470 (req_idx * MPT_SENSE_BUFFER_ALLOC));
3471
3472 sz = min_t(int, pReq->SenseBufferLength,
3473 SCSI_STD_SENSE_BYTES);
3474 memcpy(hd->pLocal->sense, sense_data, sz);
3475
3476 ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
3477 sense_data));
3478 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
3479 if (pReq->CDB[0] == INQUIRY)
3480 completionCode = MPT_SCANDV_ISSUE_SENSE;
3481 else
3482 completionCode = MPT_SCANDV_DID_RESET;
3483 }
3484 else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
3485 completionCode = MPT_SCANDV_DID_RESET;
3486 else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3487 completionCode = MPT_SCANDV_DID_RESET;
3488 else {
3489 completionCode = MPT_SCANDV_GOOD;
3490 hd->pLocal->scsiStatus = scsi_status;
3491 }
3492 break;
3493
3494 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
3495 if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3496 completionCode = MPT_SCANDV_DID_RESET;
3497 else
3498 completionCode = MPT_SCANDV_SOME_ERROR;
3499 break;
3500
3501 default:
3502 completionCode = MPT_SCANDV_SOME_ERROR;
3503 break;
3504
3505 } /* switch(status) */
3506
3507 ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n",
3508 completionCode));
3509 } /* end of address reply case */
3510
3511 hd->pLocal->completion = completionCode;
3512
3513 /* MF and RF are freed in mpt_interrupt
3514 */
3515wakeup:
3516 /* Free Chain buffers (will never chain) in scan or dv */
3517 //mptscsih_freeChainBuffers(ioc, req_idx);
3518
3519 /*
3520 * Wake up the original calling thread
3521 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003522 hd->scandv_wait_done = 1;
3523 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524
3525 return 1;
3526}
3527
3528/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3529/* mptscsih_timer_expired - Call back for timer process.
3530 * Used only for dv functionality.
3531 * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
3532 *
3533 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003534void
3535mptscsih_timer_expired(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536{
3537 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
3538
3539 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
3540
3541 if (hd->cmdPtr) {
3542 MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
3543
3544 if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
3545 /* Desire to issue a task management request here.
3546 * TM requests MUST be single threaded.
3547 * If old eh code and no TM current, issue request.
3548 * If new eh code, do nothing. Wait for OS cmd timeout
3549 * for bus reset.
3550 */
3551 ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
3552 } else {
3553 /* Perform a FW reload */
3554 if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
3555 printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
3556 }
3557 }
3558 } else {
3559 /* This should NEVER happen */
3560 printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
3561 }
3562
3563 /* No more processing.
3564 * TM call will generate an interrupt for SCSI TM Management.
3565 * The FW will reply to all outstanding commands, callback will finish cleanup.
3566 * Hard reset clean-up will free all resources.
3567 */
3568 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
3569
3570 return;
3571}
3572
3573#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3574/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3575/* mptscsih_do_raid - Format and Issue a RAID volume request message.
3576 * @hd: Pointer to scsi host structure
3577 * @action: What do be done.
3578 * @id: Logical target id.
3579 * @bus: Target locations bus.
3580 *
3581 * Returns: < 0 on a fatal error
3582 * 0 on success
3583 *
3584 * Remark: Wait to return until reply processed by the ISR.
3585 */
3586static int
3587mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io)
3588{
3589 MpiRaidActionRequest_t *pReq;
3590 MPT_FRAME_HDR *mf;
3591 int in_isr;
3592
3593 in_isr = in_interrupt();
3594 if (in_isr) {
3595 dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n",
3596 hd->ioc->name));
3597 return -EPERM;
3598 }
3599
3600 /* Get and Populate a free Frame
3601 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003602 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603 ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
3604 hd->ioc->name));
3605 return -EAGAIN;
3606 }
3607 pReq = (MpiRaidActionRequest_t *)mf;
3608 pReq->Action = action;
3609 pReq->Reserved1 = 0;
3610 pReq->ChainOffset = 0;
3611 pReq->Function = MPI_FUNCTION_RAID_ACTION;
3612 pReq->VolumeID = io->id;
3613 pReq->VolumeBus = io->bus;
3614 pReq->PhysDiskNum = io->physDiskNum;
3615 pReq->MsgFlags = 0;
3616 pReq->Reserved2 = 0;
3617 pReq->ActionDataWord = 0; /* Reserved for this action */
3618 //pReq->ActionDataSGE = 0;
3619
3620 mpt_add_sge((char *)&pReq->ActionDataSGE,
3621 MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
3622
3623 ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n",
3624 hd->ioc->name, action, io->id));
3625
3626 hd->pLocal = NULL;
3627 hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003628 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003629
3630 /* Save cmd pointer, for resource free if timeout or
3631 * FW reload occurs
3632 */
3633 hd->cmdPtr = mf;
3634
3635 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003636 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3637 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003638
3639 if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD))
3640 return -1;
3641
3642 return 0;
3643}
3644#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
3645
3646/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3647/**
3648 * mptscsih_do_cmd - Do internal command.
3649 * @hd: MPT_SCSI_HOST pointer
3650 * @io: INTERNAL_CMD pointer.
3651 *
3652 * Issue the specified internally generated command and do command
3653 * specific cleanup. For bus scan / DV only.
3654 * NOTES: If command is Inquiry and status is good,
3655 * initialize a target structure, save the data
3656 *
3657 * Remark: Single threaded access only.
3658 *
3659 * Return:
3660 * < 0 if an illegal command or no resources
3661 *
3662 * 0 if good
3663 *
3664 * > 0 if command complete but some type of completion error.
3665 */
3666static int
3667mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
3668{
3669 MPT_FRAME_HDR *mf;
3670 SCSIIORequest_t *pScsiReq;
3671 SCSIIORequest_t ReqCopy;
3672 int my_idx, ii, dir;
3673 int rc, cmdTimeout;
3674 int in_isr;
3675 char cmdLen;
3676 char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3677 char cmd = io->cmd;
3678
3679 in_isr = in_interrupt();
3680 if (in_isr) {
3681 dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
3682 hd->ioc->name));
3683 return -EPERM;
3684 }
3685
3686
3687 /* Set command specific information
3688 */
3689 switch (cmd) {
3690 case INQUIRY:
3691 cmdLen = 6;
3692 dir = MPI_SCSIIO_CONTROL_READ;
3693 CDB[0] = cmd;
3694 CDB[4] = io->size;
3695 cmdTimeout = 10;
3696 break;
3697
3698 case TEST_UNIT_READY:
3699 cmdLen = 6;
3700 dir = MPI_SCSIIO_CONTROL_READ;
3701 cmdTimeout = 10;
3702 break;
3703
3704 case START_STOP:
3705 cmdLen = 6;
3706 dir = MPI_SCSIIO_CONTROL_READ;
3707 CDB[0] = cmd;
3708 CDB[4] = 1; /*Spin up the disk */
3709 cmdTimeout = 15;
3710 break;
3711
3712 case REQUEST_SENSE:
3713 cmdLen = 6;
3714 CDB[0] = cmd;
3715 CDB[4] = io->size;
3716 dir = MPI_SCSIIO_CONTROL_READ;
3717 cmdTimeout = 10;
3718 break;
3719
3720 case READ_BUFFER:
3721 cmdLen = 10;
3722 dir = MPI_SCSIIO_CONTROL_READ;
3723 CDB[0] = cmd;
3724 if (io->flags & MPT_ICFLAG_ECHO) {
3725 CDB[1] = 0x0A;
3726 } else {
3727 CDB[1] = 0x02;
3728 }
3729
3730 if (io->flags & MPT_ICFLAG_BUF_CAP) {
3731 CDB[1] |= 0x01;
3732 }
3733 CDB[6] = (io->size >> 16) & 0xFF;
3734 CDB[7] = (io->size >> 8) & 0xFF;
3735 CDB[8] = io->size & 0xFF;
3736 cmdTimeout = 10;
3737 break;
3738
3739 case WRITE_BUFFER:
3740 cmdLen = 10;
3741 dir = MPI_SCSIIO_CONTROL_WRITE;
3742 CDB[0] = cmd;
3743 if (io->flags & MPT_ICFLAG_ECHO) {
3744 CDB[1] = 0x0A;
3745 } else {
3746 CDB[1] = 0x02;
3747 }
3748 CDB[6] = (io->size >> 16) & 0xFF;
3749 CDB[7] = (io->size >> 8) & 0xFF;
3750 CDB[8] = io->size & 0xFF;
3751 cmdTimeout = 10;
3752 break;
3753
3754 case RESERVE:
3755 cmdLen = 6;
3756 dir = MPI_SCSIIO_CONTROL_READ;
3757 CDB[0] = cmd;
3758 cmdTimeout = 10;
3759 break;
3760
3761 case RELEASE:
3762 cmdLen = 6;
3763 dir = MPI_SCSIIO_CONTROL_READ;
3764 CDB[0] = cmd;
3765 cmdTimeout = 10;
3766 break;
3767
3768 case SYNCHRONIZE_CACHE:
3769 cmdLen = 10;
3770 dir = MPI_SCSIIO_CONTROL_READ;
3771 CDB[0] = cmd;
3772// CDB[1] = 0x02; /* set immediate bit */
3773 cmdTimeout = 10;
3774 break;
3775
3776 default:
3777 /* Error Case */
3778 return -EFAULT;
3779 }
3780
3781 /* Get and Populate a free Frame
3782 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003783 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784 ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
3785 hd->ioc->name));
3786 return -EBUSY;
3787 }
3788
3789 pScsiReq = (SCSIIORequest_t *) mf;
3790
3791 /* Get the request index */
3792 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3793 ADD_INDEX_LOG(my_idx); /* for debug */
3794
3795 if (io->flags & MPT_ICFLAG_PHYS_DISK) {
3796 pScsiReq->TargetID = io->physDiskNum;
3797 pScsiReq->Bus = 0;
3798 pScsiReq->ChainOffset = 0;
3799 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
3800 } else {
3801 pScsiReq->TargetID = io->id;
3802 pScsiReq->Bus = io->bus;
3803 pScsiReq->ChainOffset = 0;
3804 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
3805 }
3806
3807 pScsiReq->CDBLength = cmdLen;
3808 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
3809
3810 pScsiReq->Reserved = 0;
3811
3812 pScsiReq->MsgFlags = mpt_msg_flags();
3813 /* MsgContext set in mpt_get_msg_fram call */
3814
3815 for (ii=0; ii < 8; ii++)
3816 pScsiReq->LUN[ii] = 0;
3817 pScsiReq->LUN[1] = io->lun;
3818
3819 if (io->flags & MPT_ICFLAG_TAGGED_CMD)
3820 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
3821 else
3822 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3823
3824 if (cmd == REQUEST_SENSE) {
3825 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3826 ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n",
3827 hd->ioc->name, cmd));
3828 }
3829
3830 for (ii=0; ii < 16; ii++)
3831 pScsiReq->CDB[ii] = CDB[ii];
3832
3833 pScsiReq->DataLength = cpu_to_le32(io->size);
3834 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
3835 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
3836
3837 ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
3838 hd->ioc->name, cmd, io->bus, io->id, io->lun));
3839
3840 if (dir == MPI_SCSIIO_CONTROL_READ) {
3841 mpt_add_sge((char *) &pScsiReq->SGL,
3842 MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
3843 io->data_dma);
3844 } else {
3845 mpt_add_sge((char *) &pScsiReq->SGL,
3846 MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
3847 io->data_dma);
3848 }
3849
3850 /* The ISR will free the request frame, but we need
3851 * the information to initialize the target. Duplicate.
3852 */
3853 memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
3854
3855 /* Issue this command after:
3856 * finish init
3857 * add timer
3858 * Wait until the reply has been received
3859 * ScsiScanDvCtx callback function will
3860 * set hd->pLocal;
3861 * set scandv_wait_done and call wake_up
3862 */
3863 hd->pLocal = NULL;
3864 hd->timer.expires = jiffies + HZ*cmdTimeout;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003865 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866
3867 /* Save cmd pointer, for resource free if timeout or
3868 * FW reload occurs
3869 */
3870 hd->cmdPtr = mf;
3871
3872 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003873 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3874 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003875
3876 if (hd->pLocal) {
3877 rc = hd->pLocal->completion;
3878 hd->pLocal->skip = 0;
3879
3880 /* Always set fatal error codes in some cases.
3881 */
3882 if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
3883 rc = -ENXIO;
3884 else if (rc == MPT_SCANDV_SOME_ERROR)
3885 rc = -rc;
3886 } else {
3887 rc = -EFAULT;
3888 /* This should never happen. */
3889 ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
3890 hd->ioc->name));
3891 }
3892
3893 return rc;
3894}
3895
3896/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3897/**
3898 * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
3899 * @hd: Pointer to MPT_SCSI_HOST structure
3900 * @portnum: IOC port number
3901 *
3902 * Uses the ISR, but with special processing.
3903 * MUST be single-threaded.
3904 *
3905 * Return: 0 on completion
3906 */
3907static int
3908mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum)
3909{
3910 MPT_ADAPTER *ioc= hd->ioc;
3911 VirtDevice *pTarget;
3912 SCSIDevicePage1_t *pcfg1Data = NULL;
3913 INTERNAL_CMD iocmd;
3914 CONFIGPARMS cfg;
3915 dma_addr_t cfg1_dma_addr = -1;
3916 ConfigPageHeader_t header1;
3917 int bus = 0;
3918 int id = 0;
3919 int lun;
3920 int indexed_lun, lun_index;
3921 int hostId = ioc->pfacts[portnum].PortSCSIID;
3922 int max_id;
3923 int requested, configuration, data;
3924 int doConfig = 0;
3925 u8 flags, factor;
3926
3927 max_id = ioc->sh->max_id - 1;
3928
3929 /* Following parameters will not change
3930 * in this routine.
3931 */
3932 iocmd.cmd = SYNCHRONIZE_CACHE;
3933 iocmd.flags = 0;
3934 iocmd.physDiskNum = -1;
3935 iocmd.data = NULL;
3936 iocmd.data_dma = -1;
3937 iocmd.size = 0;
3938 iocmd.rsvd = iocmd.rsvd2 = 0;
3939
3940 /* No SCSI hosts
3941 */
3942 if (hd->Targets == NULL)
3943 return 0;
3944
3945 /* Skip the host
3946 */
3947 if (id == hostId)
3948 id++;
3949
3950 /* Write SDP1 for all SCSI devices
3951 * Alloc memory and set up config buffer
3952 */
3953 if (ioc->bus_type == SCSI) {
3954 if (ioc->spi_data.sdp1length > 0) {
3955 pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev,
3956 ioc->spi_data.sdp1length * 4, &cfg1_dma_addr);
3957
3958 if (pcfg1Data != NULL) {
3959 doConfig = 1;
3960 header1.PageVersion = ioc->spi_data.sdp1version;
3961 header1.PageLength = ioc->spi_data.sdp1length;
3962 header1.PageNumber = 1;
3963 header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
Christoph Hellwig69218ee2005-08-18 16:26:15 +02003964 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003965 cfg.physAddr = cfg1_dma_addr;
3966 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3967 cfg.dir = 1;
3968 cfg.timeout = 0;
3969 }
3970 }
3971 }
3972
3973 /* loop through all devices on this port
3974 */
3975 while (bus < MPT_MAX_BUS) {
3976 iocmd.bus = bus;
3977 iocmd.id = id;
3978 pTarget = hd->Targets[(int)id];
3979
3980 if (doConfig) {
3981
3982 /* Set the negotiation flags */
3983 if (pTarget && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) {
3984 flags = pTarget->negoFlags;
3985 } else {
3986 flags = hd->ioc->spi_data.noQas;
3987 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3988 data = hd->ioc->spi_data.nvram[id];
3989
3990 if (data & MPT_NVRAM_WIDE_DISABLE)
3991 flags |= MPT_TARGET_NO_NEGO_WIDE;
3992
3993 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
3994 if ((factor == 0) || (factor == MPT_ASYNC))
3995 flags |= MPT_TARGET_NO_NEGO_SYNC;
3996 }
3997 }
3998
3999 /* Force to async, narrow */
4000 mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
4001 &configuration, flags);
4002 dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC "
4003 "offset=0 negoFlags=%x request=%x config=%x\n",
4004 id, flags, requested, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02004005 pcfg1Data->RequestedParameters = cpu_to_le32(requested);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004006 pcfg1Data->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02004007 pcfg1Data->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004008 cfg.pageAddr = (bus<<8) | id;
4009 mpt_config(hd->ioc, &cfg);
4010 }
4011
4012 /* If target Ptr NULL or if this target is NOT a disk, skip.
4013 */
4014 if ((pTarget) && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)){
4015 for (lun=0; lun <= MPT_LAST_LUN; lun++) {
4016 /* If LUN present, issue the command
4017 */
4018 lun_index = (lun >> 5); /* 32 luns per lun_index */
4019 indexed_lun = (lun % 32);
4020 if (pTarget->luns[lun_index] & (1<<indexed_lun)) {
4021 iocmd.lun = lun;
4022 (void) mptscsih_do_cmd(hd, &iocmd);
4023 }
4024 }
4025 }
4026
4027 /* get next relevant device */
4028 id++;
4029
4030 if (id == hostId)
4031 id++;
4032
4033 if (id > max_id) {
4034 id = 0;
4035 bus++;
4036 }
4037 }
4038
4039 if (pcfg1Data) {
4040 pci_free_consistent(ioc->pcidev, header1.PageLength * 4, pcfg1Data, cfg1_dma_addr);
4041 }
4042
4043 return 0;
4044}
4045
4046#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
4047/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4048/**
4049 * mptscsih_domainValidation - Top level handler for domain validation.
4050 * @hd: Pointer to MPT_SCSI_HOST structure.
4051 *
4052 * Uses the ISR, but with special processing.
4053 * Called from schedule, should not be in interrupt mode.
4054 * While thread alive, do dv for all devices needing dv
4055 *
4056 * Return: None.
4057 */
4058static void
4059mptscsih_domainValidation(void *arg)
4060{
4061 MPT_SCSI_HOST *hd;
4062 MPT_ADAPTER *ioc;
4063 unsigned long flags;
4064 int id, maxid, dvStatus, did;
4065 int ii, isPhysDisk;
4066
4067 spin_lock_irqsave(&dvtaskQ_lock, flags);
4068 dvtaskQ_active = 1;
4069 if (dvtaskQ_release) {
4070 dvtaskQ_active = 0;
4071 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4072 return;
4073 }
4074 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4075
4076 /* For this ioc, loop through all devices and do dv to each device.
4077 * When complete with this ioc, search through the ioc list, and
4078 * for each scsi ioc found, do dv for all devices. Exit when no
4079 * device needs dv.
4080 */
4081 did = 1;
4082 while (did) {
4083 did = 0;
4084 list_for_each_entry(ioc, &ioc_list, list) {
4085 spin_lock_irqsave(&dvtaskQ_lock, flags);
4086 if (dvtaskQ_release) {
4087 dvtaskQ_active = 0;
4088 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4089 return;
4090 }
4091 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4092
4093 msleep(250);
4094
4095 /* DV only to SCSI adapters */
4096 if (ioc->bus_type != SCSI)
4097 continue;
4098
4099 /* Make sure everything looks ok */
4100 if (ioc->sh == NULL)
4101 continue;
4102
4103 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
4104 if (hd == NULL)
4105 continue;
4106
4107 if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) {
4108 mpt_read_ioc_pg_3(ioc);
4109 if (ioc->spi_data.pIocPg3) {
4110 Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
4111 int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
4112
4113 while (numPDisk) {
4114 if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE)
4115 ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
4116
4117 pPDisk++;
4118 numPDisk--;
4119 }
4120 }
4121 ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3;
4122 }
4123
4124 maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
4125
4126 for (id = 0; id < maxid; id++) {
4127 spin_lock_irqsave(&dvtaskQ_lock, flags);
4128 if (dvtaskQ_release) {
4129 dvtaskQ_active = 0;
4130 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4131 return;
4132 }
4133 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4134 dvStatus = hd->ioc->spi_data.dvStatus[id];
4135
4136 if (dvStatus & MPT_SCSICFG_NEED_DV) {
4137 did++;
4138 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING;
4139 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV;
4140
4141 msleep(250);
4142
4143 /* If hidden phys disk, block IO's to all
4144 * raid volumes
4145 * else, process normally
4146 */
4147 isPhysDisk = mptscsih_is_phys_disk(ioc, id);
4148 if (isPhysDisk) {
4149 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4150 if (hd->ioc->spi_data.isRaid & (1 << ii)) {
4151 hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING;
4152 }
4153 }
4154 }
4155
4156 if (mptscsih_doDv(hd, 0, id) == 1) {
4157 /* Untagged device was busy, try again
4158 */
4159 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV;
4160 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING;
4161 } else {
4162 /* DV is complete. Clear flags.
4163 */
4164 hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING);
4165 }
4166
4167 if (isPhysDisk) {
4168 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4169 if (hd->ioc->spi_data.isRaid & (1 << ii)) {
4170 hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING;
4171 }
4172 }
4173 }
4174
4175 if (hd->ioc->spi_data.noQas)
4176 mptscsih_qas_check(hd, id);
4177 }
4178 }
4179 }
4180 }
4181
4182 spin_lock_irqsave(&dvtaskQ_lock, flags);
4183 dvtaskQ_active = 0;
4184 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4185
4186 return;
4187}
4188
4189/* Search IOC page 3 to determine if this is hidden physical disk
4190 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004191static int
4192mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004193{
4194 if (ioc->spi_data.pIocPg3) {
4195 Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
4196 int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
4197
4198 while (numPDisk) {
4199 if (pPDisk->PhysDiskID == id) {
4200 return 1;
4201 }
4202 pPDisk++;
4203 numPDisk--;
4204 }
4205 }
4206 return 0;
4207}
4208
4209/* Write SDP1 if no QAS has been enabled
4210 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004211static void
4212mptscsih_qas_check(MPT_SCSI_HOST *hd, int id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004213{
4214 VirtDevice *pTarget;
4215 int ii;
4216
4217 if (hd->Targets == NULL)
4218 return;
4219
4220 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4221 if (ii == id)
4222 continue;
4223
4224 if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0)
4225 continue;
4226
4227 pTarget = hd->Targets[ii];
4228
4229 if ((pTarget != NULL) && (!pTarget->raidVolume)) {
4230 if ((pTarget->negoFlags & hd->ioc->spi_data.noQas) == 0) {
4231 pTarget->negoFlags |= hd->ioc->spi_data.noQas;
4232 dnegoprintk(("writeSDP1: id=%d flags=0\n", id));
4233 mptscsih_writeSDP1(hd, 0, ii, 0);
4234 }
4235 } else {
4236 if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) {
4237 dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id));
4238 mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM);
4239 }
4240 }
4241 }
4242 return;
4243}
4244
4245
4246
4247#define MPT_GET_NVRAM_VALS 0x01
4248#define MPT_UPDATE_MAX 0x02
4249#define MPT_SET_MAX 0x04
4250#define MPT_SET_MIN 0x08
4251#define MPT_FALLBACK 0x10
4252#define MPT_SAVE 0x20
4253
4254/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4255/**
4256 * mptscsih_doDv - Perform domain validation to a target.
4257 * @hd: Pointer to MPT_SCSI_HOST structure.
4258 * @portnum: IOC port number.
4259 * @target: Physical ID of this target
4260 *
4261 * Uses the ISR, but with special processing.
4262 * MUST be single-threaded.
4263 * Test will exit if target is at async & narrow.
4264 *
4265 * Return: None.
4266 */
4267static int
4268mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id)
4269{
4270 MPT_ADAPTER *ioc = hd->ioc;
4271 VirtDevice *pTarget;
4272 SCSIDevicePage1_t *pcfg1Data;
4273 SCSIDevicePage0_t *pcfg0Data;
4274 u8 *pbuf1;
4275 u8 *pbuf2;
4276 u8 *pDvBuf;
4277 dma_addr_t dvbuf_dma = -1;
4278 dma_addr_t buf1_dma = -1;
4279 dma_addr_t buf2_dma = -1;
4280 dma_addr_t cfg1_dma_addr = -1;
4281 dma_addr_t cfg0_dma_addr = -1;
4282 ConfigPageHeader_t header1;
4283 ConfigPageHeader_t header0;
4284 DVPARAMETERS dv;
4285 INTERNAL_CMD iocmd;
4286 CONFIGPARMS cfg;
4287 int dv_alloc = 0;
4288 int rc, sz = 0;
4289 int bufsize = 0;
4290 int dataBufSize = 0;
4291 int echoBufSize = 0;
4292 int notDone;
4293 int patt;
4294 int repeat;
4295 int retcode = 0;
4296 int nfactor = MPT_ULTRA320;
4297 char firstPass = 1;
4298 char doFallback = 0;
4299 char readPage0;
4300 char bus, lun;
4301 char inq0 = 0;
4302
4303 if (ioc->spi_data.sdp1length == 0)
4304 return 0;
4305
4306 if (ioc->spi_data.sdp0length == 0)
4307 return 0;
4308
4309 /* If multiple buses are used, require that the initiator
4310 * id be the same on all buses.
4311 */
4312 if (id == ioc->pfacts[0].PortSCSIID)
4313 return 0;
4314
4315 lun = 0;
4316 bus = (u8) bus_number;
4317 ddvtprintk((MYIOC_s_NOTE_FMT
4318 "DV started: bus=%d, id=%d dv @ %p\n",
4319 ioc->name, bus, id, &dv));
4320
4321 /* Prep DV structure
4322 */
4323 memset (&dv, 0, sizeof(DVPARAMETERS));
4324 dv.id = id;
4325
4326 /* Populate tmax with the current maximum
4327 * transfer parameters for this target.
4328 * Exit if narrow and async.
4329 */
4330 dv.cmd = MPT_GET_NVRAM_VALS;
4331 mptscsih_dv_parms(hd, &dv, NULL);
4332
4333 /* Prep SCSI IO structure
4334 */
4335 iocmd.id = id;
4336 iocmd.bus = bus;
4337 iocmd.lun = lun;
4338 iocmd.flags = 0;
4339 iocmd.physDiskNum = -1;
4340 iocmd.rsvd = iocmd.rsvd2 = 0;
4341
4342 pTarget = hd->Targets[id];
4343
4344 /* Use tagged commands if possible.
4345 */
4346 if (pTarget) {
4347 if (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
4348 iocmd.flags |= MPT_ICFLAG_TAGGED_CMD;
4349 else {
4350 if (hd->ioc->facts.FWVersion.Word < 0x01000600)
4351 return 0;
4352
4353 if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4354 (hd->ioc->facts.FWVersion.Word < 0x01010B00))
4355 return 0;
4356 }
4357 }
4358
4359 /* Prep cfg structure
4360 */
4361 cfg.pageAddr = (bus<<8) | id;
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004362 cfg.cfghdr.hdr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004363
4364 /* Prep SDP0 header
4365 */
4366 header0.PageVersion = ioc->spi_data.sdp0version;
4367 header0.PageLength = ioc->spi_data.sdp0length;
4368 header0.PageNumber = 0;
4369 header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4370
4371 /* Prep SDP1 header
4372 */
4373 header1.PageVersion = ioc->spi_data.sdp1version;
4374 header1.PageLength = ioc->spi_data.sdp1length;
4375 header1.PageNumber = 1;
4376 header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4377
4378 if (header0.PageLength & 1)
4379 dv_alloc = (header0.PageLength * 4) + 4;
4380
4381 dv_alloc += (2048 + (header1.PageLength * 4));
4382
4383 pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma);
4384 if (pDvBuf == NULL)
4385 return 0;
4386
4387 sz = 0;
4388 pbuf1 = (u8 *)pDvBuf;
4389 buf1_dma = dvbuf_dma;
4390 sz +=1024;
4391
4392 pbuf2 = (u8 *) (pDvBuf + sz);
4393 buf2_dma = dvbuf_dma + sz;
4394 sz +=1024;
4395
4396 pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz);
4397 cfg0_dma_addr = dvbuf_dma + sz;
4398 sz += header0.PageLength * 4;
4399
4400 /* 8-byte alignment
4401 */
4402 if (header0.PageLength & 1)
4403 sz += 4;
4404
4405 pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz);
4406 cfg1_dma_addr = dvbuf_dma + sz;
4407
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004408 /* Skip this ID? Set cfg.cfghdr.hdr to force config page write
Linus Torvalds1da177e2005-04-16 15:20:36 -07004409 */
4410 {
4411 ScsiCfgData *pspi_data = &hd->ioc->spi_data;
4412 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
4413 /* Set the factor from nvram */
4414 nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8;
4415 if (nfactor < pspi_data->minSyncFactor )
4416 nfactor = pspi_data->minSyncFactor;
4417
4418 if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE) ||
4419 (pspi_data->PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV) ) {
4420
4421 ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
4422 ioc->name, bus, id, lun));
4423
4424 dv.cmd = MPT_SET_MAX;
4425 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004426 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004427
4428 /* Save the final negotiated settings to
4429 * SCSI device page 1.
4430 */
4431 cfg.physAddr = cfg1_dma_addr;
4432 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4433 cfg.dir = 1;
4434 mpt_config(hd->ioc, &cfg);
4435 goto target_done;
4436 }
4437 }
4438 }
4439
4440 /* Finish iocmd inititialization - hidden or visible disk? */
4441 if (ioc->spi_data.pIocPg3) {
4442 /* Search IOC page 3 for matching id
4443 */
4444 Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
4445 int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
4446
4447 while (numPDisk) {
4448 if (pPDisk->PhysDiskID == id) {
4449 /* match */
4450 iocmd.flags |= MPT_ICFLAG_PHYS_DISK;
4451 iocmd.physDiskNum = pPDisk->PhysDiskNum;
4452
4453 /* Quiesce the IM
4454 */
4455 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) {
4456 ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
4457 goto target_done;
4458 }
4459 break;
4460 }
4461 pPDisk++;
4462 numPDisk--;
4463 }
4464 }
4465
4466 /* RAID Volume ID's may double for a physical device. If RAID but
4467 * not a physical ID as well, skip DV.
4468 */
4469 if ((hd->ioc->spi_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK))
4470 goto target_done;
4471
4472
4473 /* Basic Test.
4474 * Async & Narrow - Inquiry
4475 * Async & Narrow - Inquiry
4476 * Maximum transfer rate - Inquiry
4477 * Compare buffers:
4478 * If compare, test complete.
4479 * If miscompare and first pass, repeat
4480 * If miscompare and not first pass, fall back and repeat
4481 */
4482 hd->pLocal = NULL;
4483 readPage0 = 0;
4484 sz = SCSI_MAX_INQUIRY_BYTES;
4485 rc = MPT_SCANDV_GOOD;
4486 while (1) {
4487 ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id));
4488 retcode = 0;
4489 dv.cmd = MPT_SET_MIN;
4490 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4491
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004492 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004493 cfg.physAddr = cfg1_dma_addr;
4494 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4495 cfg.dir = 1;
4496 if (mpt_config(hd->ioc, &cfg) != 0)
4497 goto target_done;
4498
4499 /* Wide - narrow - wide workaround case
4500 */
4501 if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
4502 /* Send an untagged command to reset disk Qs corrupted
4503 * when a parity error occurs on a Request Sense.
4504 */
4505 if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
4506 ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4507 (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) {
4508
4509 iocmd.cmd = REQUEST_SENSE;
4510 iocmd.data_dma = buf1_dma;
4511 iocmd.data = pbuf1;
4512 iocmd.size = 0x12;
4513 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4514 goto target_done;
4515 else {
4516 if (hd->pLocal == NULL)
4517 goto target_done;
4518 rc = hd->pLocal->completion;
4519 if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) {
4520 dv.max.width = 0;
4521 doFallback = 0;
4522 } else
4523 goto target_done;
4524 }
4525 } else
4526 goto target_done;
4527 }
4528
4529 iocmd.cmd = INQUIRY;
4530 iocmd.data_dma = buf1_dma;
4531 iocmd.data = pbuf1;
4532 iocmd.size = sz;
4533 memset(pbuf1, 0x00, sz);
4534 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4535 goto target_done;
4536 else {
4537 if (hd->pLocal == NULL)
4538 goto target_done;
4539 rc = hd->pLocal->completion;
4540 if (rc == MPT_SCANDV_GOOD) {
4541 if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) {
4542 if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
4543 retcode = 1;
4544 else
4545 retcode = 0;
4546
4547 goto target_done;
4548 }
4549 } else if (rc == MPT_SCANDV_SENSE) {
4550 ;
4551 } else {
4552 /* If first command doesn't complete
4553 * with a good status or with a check condition,
4554 * exit.
4555 */
4556 goto target_done;
4557 }
4558 }
4559
4560 /* Reset the size for disks
4561 */
4562 inq0 = (*pbuf1) & 0x1F;
4563 if ((inq0 == 0) && pTarget && !pTarget->raidVolume) {
4564 sz = 0x40;
4565 iocmd.size = sz;
4566 }
4567
4568 /* Another GEM workaround. Check peripheral device type,
4569 * if PROCESSOR, quit DV.
4570 */
4571 if (inq0 == TYPE_PROCESSOR) {
4572 mptscsih_initTarget(hd,
4573 bus,
4574 id,
4575 lun,
4576 pbuf1,
4577 sz);
4578 goto target_done;
4579 }
4580
4581 if (inq0 > 0x08)
4582 goto target_done;
4583
4584 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4585 goto target_done;
4586
4587 if (sz == 0x40) {
4588 if ((pTarget->maxWidth == 1) && (pTarget->maxOffset) && (nfactor < 0x0A)
4589 && (pTarget->minSyncFactor > 0x09)) {
4590 if ((pbuf1[56] & 0x04) == 0)
4591 ;
4592 else if ((pbuf1[56] & 0x01) == 1) {
4593 pTarget->minSyncFactor =
4594 nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320;
4595 } else {
4596 pTarget->minSyncFactor =
4597 nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160;
4598 }
4599
4600 dv.max.factor = pTarget->minSyncFactor;
4601
4602 if ((pbuf1[56] & 0x02) == 0) {
4603 pTarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
4604 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004605 ddvprintk((MYIOC_s_NOTE_FMT
4606 "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004607 ioc->name, id, pbuf1[56]));
4608 }
4609 }
4610 }
4611
4612 if (doFallback)
4613 dv.cmd = MPT_FALLBACK;
4614 else
4615 dv.cmd = MPT_SET_MAX;
4616
4617 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4618 if (mpt_config(hd->ioc, &cfg) != 0)
4619 goto target_done;
4620
4621 if ((!dv.now.width) && (!dv.now.offset))
4622 goto target_done;
4623
4624 iocmd.cmd = INQUIRY;
4625 iocmd.data_dma = buf2_dma;
4626 iocmd.data = pbuf2;
4627 iocmd.size = sz;
4628 memset(pbuf2, 0x00, sz);
4629 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4630 goto target_done;
4631 else if (hd->pLocal == NULL)
4632 goto target_done;
4633 else {
4634 /* Save the return code.
4635 * If this is the first pass,
4636 * read SCSI Device Page 0
4637 * and update the target max parameters.
4638 */
4639 rc = hd->pLocal->completion;
4640 doFallback = 0;
4641 if (rc == MPT_SCANDV_GOOD) {
4642 if (!readPage0) {
4643 u32 sdp0_info;
4644 u32 sdp0_nego;
4645
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004646 cfg.cfghdr.hdr = &header0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004647 cfg.physAddr = cfg0_dma_addr;
4648 cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
4649 cfg.dir = 0;
4650
4651 if (mpt_config(hd->ioc, &cfg) != 0)
4652 goto target_done;
4653
4654 sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E;
4655 sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8;
4656
4657 /* Quantum and Fujitsu workarounds.
4658 * Quantum: PPR U320 -> PPR reply with Ultra2 and wide
4659 * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
4660 * Resetart with a request for U160.
4661 */
4662 if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
4663 doFallback = 1;
4664 } else {
4665 dv.cmd = MPT_UPDATE_MAX;
4666 mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data);
4667 /* Update the SCSI device page 1 area
4668 */
4669 pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters;
4670 readPage0 = 1;
4671 }
4672 }
4673
4674 /* Quantum workaround. Restart this test will the fallback
4675 * flag set.
4676 */
4677 if (doFallback == 0) {
4678 if (memcmp(pbuf1, pbuf2, sz) != 0) {
4679 if (!firstPass)
4680 doFallback = 1;
4681 } else {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004682 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07004683 "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id));
4684 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE;
4685 mptscsih_initTarget(hd,
4686 bus,
4687 id,
4688 lun,
4689 pbuf1,
4690 sz);
4691 break; /* test complete */
4692 }
4693 }
4694
4695
4696 } else if (rc == MPT_SCANDV_ISSUE_SENSE)
4697 doFallback = 1; /* set fallback flag */
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004698 else if ((rc == MPT_SCANDV_DID_RESET) ||
4699 (rc == MPT_SCANDV_SENSE) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004700 (rc == MPT_SCANDV_FALLBACK))
4701 doFallback = 1; /* set fallback flag */
4702 else
4703 goto target_done;
4704
4705 firstPass = 0;
4706 }
4707 }
4708 ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id));
4709
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004710 if (ioc->spi_data.mpt_dv == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004711 goto target_done;
4712
4713 inq0 = (*pbuf1) & 0x1F;
4714
4715 /* Continue only for disks
4716 */
4717 if (inq0 != 0)
4718 goto target_done;
4719
4720 if ( ioc->spi_data.PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY )
4721 goto target_done;
4722
4723 /* Start the Enhanced Test.
4724 * 0) issue TUR to clear out check conditions
4725 * 1) read capacity of echo (regular) buffer
4726 * 2) reserve device
4727 * 3) do write-read-compare data pattern test
4728 * 4) release
4729 * 5) update nego parms to target struct
4730 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004731 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004732 cfg.physAddr = cfg1_dma_addr;
4733 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4734 cfg.dir = 1;
4735
4736 iocmd.cmd = TEST_UNIT_READY;
4737 iocmd.data_dma = -1;
4738 iocmd.data = NULL;
4739 iocmd.size = 0;
4740 notDone = 1;
4741 while (notDone) {
4742 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4743 goto target_done;
4744
4745 if (hd->pLocal == NULL)
4746 goto target_done;
4747
4748 rc = hd->pLocal->completion;
4749 if (rc == MPT_SCANDV_GOOD)
4750 notDone = 0;
4751 else if (rc == MPT_SCANDV_SENSE) {
4752 u8 skey = hd->pLocal->sense[2] & 0x0F;
4753 u8 asc = hd->pLocal->sense[12];
4754 u8 ascq = hd->pLocal->sense[13];
4755 ddvprintk((MYIOC_s_INFO_FMT
4756 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4757 ioc->name, skey, asc, ascq));
4758
4759 if (skey == UNIT_ATTENTION)
4760 notDone++; /* repeat */
4761 else if ((skey == NOT_READY) &&
4762 (asc == 0x04)&&(ascq == 0x01)) {
4763 /* wait then repeat */
4764 mdelay (2000);
4765 notDone++;
4766 } else if ((skey == NOT_READY) && (asc == 0x3A)) {
4767 /* no medium, try read test anyway */
4768 notDone = 0;
4769 } else {
4770 /* All other errors are fatal.
4771 */
4772 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4773 ioc->name));
4774 goto target_done;
4775 }
4776 } else
4777 goto target_done;
4778 }
4779
4780 iocmd.cmd = READ_BUFFER;
4781 iocmd.data_dma = buf1_dma;
4782 iocmd.data = pbuf1;
4783 iocmd.size = 4;
4784 iocmd.flags |= MPT_ICFLAG_BUF_CAP;
4785
4786 dataBufSize = 0;
4787 echoBufSize = 0;
4788 for (patt = 0; patt < 2; patt++) {
4789 if (patt == 0)
4790 iocmd.flags |= MPT_ICFLAG_ECHO;
4791 else
4792 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4793
4794 notDone = 1;
4795 while (notDone) {
4796 bufsize = 0;
4797
4798 /* If not ready after 8 trials,
4799 * give up on this device.
4800 */
4801 if (notDone > 8)
4802 goto target_done;
4803
4804 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4805 goto target_done;
4806 else if (hd->pLocal == NULL)
4807 goto target_done;
4808 else {
4809 rc = hd->pLocal->completion;
4810 ddvprintk(("ReadBuffer Comp Code %d", rc));
4811 ddvprintk((" buff: %0x %0x %0x %0x\n",
4812 pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3]));
4813
4814 if (rc == MPT_SCANDV_GOOD) {
4815 notDone = 0;
4816 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4817 bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3];
4818 } else {
4819 bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3];
4820 }
4821 } else if (rc == MPT_SCANDV_SENSE) {
4822 u8 skey = hd->pLocal->sense[2] & 0x0F;
4823 u8 asc = hd->pLocal->sense[12];
4824 u8 ascq = hd->pLocal->sense[13];
4825 ddvprintk((MYIOC_s_INFO_FMT
4826 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4827 ioc->name, skey, asc, ascq));
4828 if (skey == ILLEGAL_REQUEST) {
4829 notDone = 0;
4830 } else if (skey == UNIT_ATTENTION) {
4831 notDone++; /* repeat */
4832 } else if ((skey == NOT_READY) &&
4833 (asc == 0x04)&&(ascq == 0x01)) {
4834 /* wait then repeat */
4835 mdelay (2000);
4836 notDone++;
4837 } else {
4838 /* All other errors are fatal.
4839 */
4840 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4841 ioc->name));
4842 goto target_done;
4843 }
4844 } else {
4845 /* All other errors are fatal
4846 */
4847 goto target_done;
4848 }
4849 }
4850 }
4851
4852 if (iocmd.flags & MPT_ICFLAG_ECHO)
4853 echoBufSize = bufsize;
4854 else
4855 dataBufSize = bufsize;
4856 }
4857 sz = 0;
4858 iocmd.flags &= ~MPT_ICFLAG_BUF_CAP;
4859
4860 /* Use echo buffers if possible,
4861 * Exit if both buffers are 0.
4862 */
4863 if (echoBufSize > 0) {
4864 iocmd.flags |= MPT_ICFLAG_ECHO;
4865 if (dataBufSize > 0)
4866 bufsize = min(echoBufSize, dataBufSize);
4867 else
4868 bufsize = echoBufSize;
4869 } else if (dataBufSize == 0)
4870 goto target_done;
4871
4872 ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name,
4873 (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize));
4874
4875 /* Data buffers for write-read-compare test max 1K.
4876 */
4877 sz = min(bufsize, 1024);
4878
4879 /* --- loop ----
4880 * On first pass, always issue a reserve.
4881 * On additional loops, only if a reset has occurred.
4882 * iocmd.flags indicates if echo or regular buffer
4883 */
4884 for (patt = 0; patt < 4; patt++) {
4885 ddvprintk(("Pattern %d\n", patt));
4886 if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) {
4887 iocmd.cmd = TEST_UNIT_READY;
4888 iocmd.data_dma = -1;
4889 iocmd.data = NULL;
4890 iocmd.size = 0;
4891 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4892 goto target_done;
4893
4894 iocmd.cmd = RELEASE;
4895 iocmd.data_dma = -1;
4896 iocmd.data = NULL;
4897 iocmd.size = 0;
4898 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4899 goto target_done;
4900 else if (hd->pLocal == NULL)
4901 goto target_done;
4902 else {
4903 rc = hd->pLocal->completion;
4904 ddvprintk(("Release rc %d\n", rc));
4905 if (rc == MPT_SCANDV_GOOD)
4906 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4907 else
4908 goto target_done;
4909 }
4910 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4911 }
4912 iocmd.flags &= ~MPT_ICFLAG_DID_RESET;
4913
4914 repeat = 5;
4915 while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) {
4916 iocmd.cmd = RESERVE;
4917 iocmd.data_dma = -1;
4918 iocmd.data = NULL;
4919 iocmd.size = 0;
4920 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4921 goto target_done;
4922 else if (hd->pLocal == NULL)
4923 goto target_done;
4924 else {
4925 rc = hd->pLocal->completion;
4926 if (rc == MPT_SCANDV_GOOD) {
4927 iocmd.flags |= MPT_ICFLAG_RESERVED;
4928 } else if (rc == MPT_SCANDV_SENSE) {
4929 /* Wait if coming ready
4930 */
4931 u8 skey = hd->pLocal->sense[2] & 0x0F;
4932 u8 asc = hd->pLocal->sense[12];
4933 u8 ascq = hd->pLocal->sense[13];
4934 ddvprintk((MYIOC_s_INFO_FMT
4935 "DV: Reserve Failed: ", ioc->name));
4936 ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4937 skey, asc, ascq));
4938
4939 if ((skey == NOT_READY) && (asc == 0x04)&&
4940 (ascq == 0x01)) {
4941 /* wait then repeat */
4942 mdelay (2000);
4943 notDone++;
4944 } else {
4945 ddvprintk((MYIOC_s_INFO_FMT
4946 "DV: Reserved Failed.", ioc->name));
4947 goto target_done;
4948 }
4949 } else {
4950 ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.",
4951 ioc->name));
4952 goto target_done;
4953 }
4954 }
4955 }
4956
4957 mptscsih_fillbuf(pbuf1, sz, patt, 1);
4958 iocmd.cmd = WRITE_BUFFER;
4959 iocmd.data_dma = buf1_dma;
4960 iocmd.data = pbuf1;
4961 iocmd.size = sz;
4962 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4963 goto target_done;
4964 else if (hd->pLocal == NULL)
4965 goto target_done;
4966 else {
4967 rc = hd->pLocal->completion;
4968 if (rc == MPT_SCANDV_GOOD)
4969 ; /* Issue read buffer */
4970 else if (rc == MPT_SCANDV_DID_RESET) {
4971 /* If using echo buffers, reset to data buffers.
4972 * Else do Fallback and restart
4973 * this test (re-issue reserve
4974 * because of bus reset).
4975 */
4976 if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) {
4977 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4978 } else {
4979 dv.cmd = MPT_FALLBACK;
4980 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4981
4982 if (mpt_config(hd->ioc, &cfg) != 0)
4983 goto target_done;
4984
4985 if ((!dv.now.width) && (!dv.now.offset))
4986 goto target_done;
4987 }
4988
4989 iocmd.flags |= MPT_ICFLAG_DID_RESET;
4990 patt = -1;
4991 continue;
4992 } else if (rc == MPT_SCANDV_SENSE) {
4993 /* Restart data test if UA, else quit.
4994 */
4995 u8 skey = hd->pLocal->sense[2] & 0x0F;
4996 ddvprintk((MYIOC_s_INFO_FMT
4997 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
4998 hd->pLocal->sense[12], hd->pLocal->sense[13]));
4999 if (skey == UNIT_ATTENTION) {
5000 patt = -1;
5001 continue;
5002 } else if (skey == ILLEGAL_REQUEST) {
5003 if (iocmd.flags & MPT_ICFLAG_ECHO) {
5004 if (dataBufSize >= bufsize) {
5005 iocmd.flags &= ~MPT_ICFLAG_ECHO;
5006 patt = -1;
5007 continue;
5008 }
5009 }
5010 goto target_done;
5011 }
5012 else
5013 goto target_done;
5014 } else {
5015 /* fatal error */
5016 goto target_done;
5017 }
5018 }
5019
5020 iocmd.cmd = READ_BUFFER;
5021 iocmd.data_dma = buf2_dma;
5022 iocmd.data = pbuf2;
5023 iocmd.size = sz;
5024 if (mptscsih_do_cmd(hd, &iocmd) < 0)
5025 goto target_done;
5026 else if (hd->pLocal == NULL)
5027 goto target_done;
5028 else {
5029 rc = hd->pLocal->completion;
5030 if (rc == MPT_SCANDV_GOOD) {
5031 /* If buffers compare,
5032 * go to next pattern,
5033 * else, do a fallback and restart
5034 * data transfer test.
5035 */
5036 if (memcmp (pbuf1, pbuf2, sz) == 0) {
5037 ; /* goto next pattern */
5038 } else {
5039 /* Miscompare with Echo buffer, go to data buffer,
5040 * if that buffer exists.
5041 * Miscompare with Data buffer, check first 4 bytes,
5042 * some devices return capacity. Exit in this case.
5043 */
5044 if (iocmd.flags & MPT_ICFLAG_ECHO) {
5045 if (dataBufSize >= bufsize)
5046 iocmd.flags &= ~MPT_ICFLAG_ECHO;
5047 else
5048 goto target_done;
5049 } else {
5050 if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) {
5051 /* Argh. Device returning wrong data.
5052 * Quit DV for this device.
5053 */
5054 goto target_done;
5055 }
5056
5057 /* Had an actual miscompare. Slow down.*/
5058 dv.cmd = MPT_FALLBACK;
5059 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5060
5061 if (mpt_config(hd->ioc, &cfg) != 0)
5062 goto target_done;
5063
5064 if ((!dv.now.width) && (!dv.now.offset))
5065 goto target_done;
5066 }
5067
5068 patt = -1;
5069 continue;
5070 }
5071 } else if (rc == MPT_SCANDV_DID_RESET) {
5072 /* Do Fallback and restart
5073 * this test (re-issue reserve
5074 * because of bus reset).
5075 */
5076 dv.cmd = MPT_FALLBACK;
5077 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5078
5079 if (mpt_config(hd->ioc, &cfg) != 0)
5080 goto target_done;
5081
5082 if ((!dv.now.width) && (!dv.now.offset))
5083 goto target_done;
5084
5085 iocmd.flags |= MPT_ICFLAG_DID_RESET;
5086 patt = -1;
5087 continue;
5088 } else if (rc == MPT_SCANDV_SENSE) {
5089 /* Restart data test if UA, else quit.
5090 */
5091 u8 skey = hd->pLocal->sense[2] & 0x0F;
5092 ddvprintk((MYIOC_s_INFO_FMT
5093 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
5094 hd->pLocal->sense[12], hd->pLocal->sense[13]));
5095 if (skey == UNIT_ATTENTION) {
5096 patt = -1;
5097 continue;
5098 }
5099 else
5100 goto target_done;
5101 } else {
5102 /* fatal error */
5103 goto target_done;
5104 }
5105 }
5106
5107 } /* --- end of patt loop ---- */
5108
5109target_done:
5110 if (iocmd.flags & MPT_ICFLAG_RESERVED) {
5111 iocmd.cmd = RELEASE;
5112 iocmd.data_dma = -1;
5113 iocmd.data = NULL;
5114 iocmd.size = 0;
5115 if (mptscsih_do_cmd(hd, &iocmd) < 0)
5116 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5117 ioc->name, id);
5118 else if (hd->pLocal) {
5119 if (hd->pLocal->completion == MPT_SCANDV_GOOD)
5120 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
5121 } else {
5122 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5123 ioc->name, id);
5124 }
5125 }
5126
5127
5128 /* Set if cfg1_dma_addr contents is valid
5129 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005130 if ((cfg.cfghdr.hdr != NULL) && (retcode == 0)){
Linus Torvalds1da177e2005-04-16 15:20:36 -07005131 /* If disk, not U320, disable QAS
5132 */
5133 if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) {
5134 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02005135 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07005136 "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor));
5137 }
5138
5139 dv.cmd = MPT_SAVE;
5140 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5141
5142 /* Double writes to SDP1 can cause problems,
5143 * skip save of the final negotiated settings to
5144 * SCSI device page 1.
5145 *
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005146 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005147 cfg.physAddr = cfg1_dma_addr;
5148 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
5149 cfg.dir = 1;
5150 mpt_config(hd->ioc, &cfg);
5151 */
5152 }
5153
5154 /* If this is a RAID Passthrough, enable internal IOs
5155 */
5156 if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) {
5157 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0)
5158 ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name));
5159 }
5160
5161 /* Done with the DV scan of the current target
5162 */
5163 if (pDvBuf)
5164 pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma);
5165
5166 ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n",
5167 ioc->name, id));
5168
5169 return retcode;
5170}
5171
5172/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5173/* mptscsih_dv_parms - perform a variety of operations on the
5174 * parameters used for negotiation.
5175 * @hd: Pointer to a SCSI host.
5176 * @dv: Pointer to a structure that contains the maximum and current
5177 * negotiated parameters.
5178 */
5179static void
5180mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage)
5181{
5182 VirtDevice *pTarget;
5183 SCSIDevicePage0_t *pPage0;
5184 SCSIDevicePage1_t *pPage1;
5185 int val = 0, data, configuration;
5186 u8 width = 0;
5187 u8 offset = 0;
5188 u8 factor = 0;
5189 u8 negoFlags = 0;
5190 u8 cmd = dv->cmd;
5191 u8 id = dv->id;
5192
5193 switch (cmd) {
5194 case MPT_GET_NVRAM_VALS:
5195 ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ",
5196 hd->ioc->name));
5197 /* Get the NVRAM values and save in tmax
5198 * If not an LVD bus, the adapter minSyncFactor has been
5199 * already throttled back.
5200 */
5201 if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume) {
5202 width = pTarget->maxWidth;
5203 offset = pTarget->maxOffset;
5204 factor = pTarget->minSyncFactor;
5205 negoFlags = pTarget->negoFlags;
5206 } else {
5207 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
5208 data = hd->ioc->spi_data.nvram[id];
5209 width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
5210 if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0)
5211 factor = MPT_ASYNC;
5212 else {
5213 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
5214 if ((factor == 0) || (factor == MPT_ASYNC)){
5215 factor = MPT_ASYNC;
5216 offset = 0;
5217 }
5218 }
5219 } else {
5220 width = MPT_NARROW;
5221 offset = 0;
5222 factor = MPT_ASYNC;
5223 }
5224
5225 /* Set the negotiation flags */
5226 negoFlags = hd->ioc->spi_data.noQas;
5227 if (!width)
5228 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
5229
5230 if (!offset)
5231 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
5232 }
5233
5234 /* limit by adapter capabilities */
5235 width = min(width, hd->ioc->spi_data.maxBusWidth);
5236 offset = min(offset, hd->ioc->spi_data.maxSyncOffset);
5237 factor = max(factor, hd->ioc->spi_data.minSyncFactor);
5238
5239 /* Check Consistency */
5240 if (offset && (factor < MPT_ULTRA2) && !width)
5241 factor = MPT_ULTRA2;
5242
5243 dv->max.width = width;
5244 dv->max.offset = offset;
5245 dv->max.factor = factor;
5246 dv->max.flags = negoFlags;
5247 ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n",
5248 id, width, factor, offset, negoFlags));
5249 break;
5250
5251 case MPT_UPDATE_MAX:
5252 ddvprintk((MYIOC_s_NOTE_FMT
5253 "Updating with SDP0 Data: ", hd->ioc->name));
5254 /* Update tmax values with those from Device Page 0.*/
5255 pPage0 = (SCSIDevicePage0_t *) pPage;
5256 if (pPage0) {
Christoph Hellwig637fa992005-08-18 16:25:44 +02005257 val = le32_to_cpu(pPage0->NegotiatedParameters);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005258 dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0;
5259 dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16;
5260 dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8;
5261 }
5262
5263 dv->now.width = dv->max.width;
5264 dv->now.offset = dv->max.offset;
5265 dv->now.factor = dv->max.factor;
5266 ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n",
5267 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5268 break;
5269
5270 case MPT_SET_MAX:
5271 ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ",
5272 hd->ioc->name));
5273 /* Set current to the max values. Update the config page.*/
5274 dv->now.width = dv->max.width;
5275 dv->now.offset = dv->max.offset;
5276 dv->now.factor = dv->max.factor;
5277 dv->now.flags = dv->max.flags;
5278
5279 pPage1 = (SCSIDevicePage1_t *)pPage;
5280 if (pPage1) {
5281 mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor,
5282 dv->now.offset, &val, &configuration, dv->now.flags);
5283 dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5284 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005285 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005286 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005287 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005288 }
5289
Christoph Hellwig637fa992005-08-18 16:25:44 +02005290 ddvprintk(("id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x configuration=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005291 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
5292 break;
5293
5294 case MPT_SET_MIN:
5295 ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
5296 hd->ioc->name));
5297 /* Set page to asynchronous and narrow
5298 * Do not update now, breaks fallback routine. */
5299 width = MPT_NARROW;
5300 offset = 0;
5301 factor = MPT_ASYNC;
5302 negoFlags = dv->max.flags;
5303
5304 pPage1 = (SCSIDevicePage1_t *)pPage;
5305 if (pPage1) {
5306 mptscsih_setDevicePage1Flags (width, factor,
5307 offset, &val, &configuration, negoFlags);
5308 dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5309 id, width, factor, offset, negoFlags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005310 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005311 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005312 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005313 }
5314 ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n",
5315 id, width, factor, offset, val, configuration, negoFlags));
5316 break;
5317
5318 case MPT_FALLBACK:
5319 ddvprintk((MYIOC_s_NOTE_FMT
5320 "Fallback: Start: offset %d, factor %x, width %d \n",
5321 hd->ioc->name, dv->now.offset,
5322 dv->now.factor, dv->now.width));
5323 width = dv->now.width;
5324 offset = dv->now.offset;
5325 factor = dv->now.factor;
5326 if ((offset) && (dv->max.width)) {
5327 if (factor < MPT_ULTRA160)
5328 factor = MPT_ULTRA160;
5329 else if (factor < MPT_ULTRA2) {
5330 factor = MPT_ULTRA2;
5331 width = MPT_WIDE;
5332 } else if ((factor == MPT_ULTRA2) && width) {
5333 factor = MPT_ULTRA2;
5334 width = MPT_NARROW;
5335 } else if (factor < MPT_ULTRA) {
5336 factor = MPT_ULTRA;
5337 width = MPT_WIDE;
5338 } else if ((factor == MPT_ULTRA) && width) {
5339 width = MPT_NARROW;
5340 } else if (factor < MPT_FAST) {
5341 factor = MPT_FAST;
5342 width = MPT_WIDE;
5343 } else if ((factor == MPT_FAST) && width) {
5344 factor = MPT_FAST;
5345 width = MPT_NARROW;
5346 } else if (factor < MPT_SCSI) {
5347 factor = MPT_SCSI;
5348 width = MPT_WIDE;
5349 } else if ((factor == MPT_SCSI) && width) {
5350 factor = MPT_SCSI;
5351 width = MPT_NARROW;
5352 } else {
5353 factor = MPT_ASYNC;
5354 offset = 0;
5355 }
5356
5357 } else if (offset) {
5358 width = MPT_NARROW;
5359 if (factor < MPT_ULTRA)
5360 factor = MPT_ULTRA;
5361 else if (factor < MPT_FAST)
5362 factor = MPT_FAST;
5363 else if (factor < MPT_SCSI)
5364 factor = MPT_SCSI;
5365 else {
5366 factor = MPT_ASYNC;
5367 offset = 0;
5368 }
5369
5370 } else {
5371 width = MPT_NARROW;
5372 factor = MPT_ASYNC;
5373 }
5374 dv->max.flags |= MPT_TARGET_NO_NEGO_QAS;
5375 dv->max.flags &= ~MPT_TAPE_NEGO_IDP;
5376
5377 dv->now.width = width;
5378 dv->now.offset = offset;
5379 dv->now.factor = factor;
5380 dv->now.flags = dv->max.flags;
5381
5382 pPage1 = (SCSIDevicePage1_t *)pPage;
5383 if (pPage1) {
5384 mptscsih_setDevicePage1Flags (width, factor, offset, &val,
5385 &configuration, dv->now.flags);
Christoph Hellwig637fa992005-08-18 16:25:44 +02005386 dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x negoFlags=%x request=%x config=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005387 id, width, offset, factor, dv->now.flags, val, configuration));
5388
Christoph Hellwig637fa992005-08-18 16:25:44 +02005389 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005390 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005391 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005392 }
5393
5394 ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n",
5395 id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration));
5396 break;
5397
5398 case MPT_SAVE:
5399 ddvprintk((MYIOC_s_NOTE_FMT
5400 "Saving to Target structure: ", hd->ioc->name));
5401 ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n",
5402 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5403
5404 /* Save these values to target structures
5405 * or overwrite nvram (phys disks only).
5406 */
5407
5408 if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume ) {
5409 pTarget->maxWidth = dv->now.width;
5410 pTarget->maxOffset = dv->now.offset;
5411 pTarget->minSyncFactor = dv->now.factor;
5412 pTarget->negoFlags = dv->now.flags;
5413 } else {
5414 /* Preserv all flags, use
5415 * read-modify-write algorithm
5416 */
5417 if (hd->ioc->spi_data.nvram) {
5418 data = hd->ioc->spi_data.nvram[id];
5419
5420 if (dv->now.width)
5421 data &= ~MPT_NVRAM_WIDE_DISABLE;
5422 else
5423 data |= MPT_NVRAM_WIDE_DISABLE;
5424
5425 if (!dv->now.offset)
5426 factor = MPT_ASYNC;
5427
5428 data &= ~MPT_NVRAM_SYNC_MASK;
5429 data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK;
5430
5431 hd->ioc->spi_data.nvram[id] = data;
5432 }
5433 }
5434 break;
5435 }
5436}
5437
5438/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5439/* mptscsih_fillbuf - fill a buffer with a special data pattern
5440 * cleanup. For bus scan only.
5441 *
5442 * @buffer: Pointer to data buffer to be filled.
5443 * @size: Number of bytes to fill
5444 * @index: Pattern index
5445 * @width: bus width, 0 (8 bits) or 1 (16 bits)
5446 */
5447static void
5448mptscsih_fillbuf(char *buffer, int size, int index, int width)
5449{
5450 char *ptr = buffer;
5451 int ii;
5452 char byte;
5453 short val;
5454
5455 switch (index) {
5456 case 0:
5457
5458 if (width) {
5459 /* Pattern: 0000 FFFF 0000 FFFF
5460 */
5461 for (ii=0; ii < size; ii++, ptr++) {
5462 if (ii & 0x02)
5463 *ptr = 0xFF;
5464 else
5465 *ptr = 0x00;
5466 }
5467 } else {
5468 /* Pattern: 00 FF 00 FF
5469 */
5470 for (ii=0; ii < size; ii++, ptr++) {
5471 if (ii & 0x01)
5472 *ptr = 0xFF;
5473 else
5474 *ptr = 0x00;
5475 }
5476 }
5477 break;
5478
5479 case 1:
5480 if (width) {
5481 /* Pattern: 5555 AAAA 5555 AAAA 5555
5482 */
5483 for (ii=0; ii < size; ii++, ptr++) {
5484 if (ii & 0x02)
5485 *ptr = 0xAA;
5486 else
5487 *ptr = 0x55;
5488 }
5489 } else {
5490 /* Pattern: 55 AA 55 AA 55
5491 */
5492 for (ii=0; ii < size; ii++, ptr++) {
5493 if (ii & 0x01)
5494 *ptr = 0xAA;
5495 else
5496 *ptr = 0x55;
5497 }
5498 }
5499 break;
5500
5501 case 2:
5502 /* Pattern: 00 01 02 03 04 05
5503 * ... FE FF 00 01..
5504 */
5505 for (ii=0; ii < size; ii++, ptr++)
5506 *ptr = (char) ii;
5507 break;
5508
5509 case 3:
5510 if (width) {
5511 /* Wide Pattern: FFFE 0001 FFFD 0002
5512 * ... 4000 DFFF 8000 EFFF
5513 */
5514 byte = 0;
5515 for (ii=0; ii < size/2; ii++) {
5516 /* Create the base pattern
5517 */
5518 val = (1 << byte);
5519 /* every 64 (0x40) bytes flip the pattern
5520 * since we fill 2 bytes / iteration,
5521 * test for ii = 0x20
5522 */
5523 if (ii & 0x20)
5524 val = ~(val);
5525
5526 if (ii & 0x01) {
5527 *ptr = (char)( (val & 0xFF00) >> 8);
5528 ptr++;
5529 *ptr = (char)(val & 0xFF);
5530 byte++;
5531 byte &= 0x0F;
5532 } else {
5533 val = ~val;
5534 *ptr = (char)( (val & 0xFF00) >> 8);
5535 ptr++;
5536 *ptr = (char)(val & 0xFF);
5537 }
5538
5539 ptr++;
5540 }
5541 } else {
5542 /* Narrow Pattern: FE 01 FD 02 FB 04
5543 * .. 7F 80 01 FE 02 FD ... 80 7F
5544 */
5545 byte = 0;
5546 for (ii=0; ii < size; ii++, ptr++) {
5547 /* Base pattern - first 32 bytes
5548 */
5549 if (ii & 0x01) {
5550 *ptr = (1 << byte);
5551 byte++;
5552 byte &= 0x07;
5553 } else {
5554 *ptr = (char) (~(1 << byte));
5555 }
5556
5557 /* Flip the pattern every 32 bytes
5558 */
5559 if (ii & 0x20)
5560 *ptr = ~(*ptr);
5561 }
5562 }
5563 break;
5564 }
5565}
5566#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
5567
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005568EXPORT_SYMBOL(mptscsih_remove);
5569EXPORT_SYMBOL(mptscsih_shutdown);
5570#ifdef CONFIG_PM
5571EXPORT_SYMBOL(mptscsih_suspend);
5572EXPORT_SYMBOL(mptscsih_resume);
5573#endif
5574EXPORT_SYMBOL(mptscsih_proc_info);
5575EXPORT_SYMBOL(mptscsih_info);
5576EXPORT_SYMBOL(mptscsih_qcmd);
5577EXPORT_SYMBOL(mptscsih_slave_alloc);
5578EXPORT_SYMBOL(mptscsih_slave_destroy);
5579EXPORT_SYMBOL(mptscsih_slave_configure);
5580EXPORT_SYMBOL(mptscsih_abort);
5581EXPORT_SYMBOL(mptscsih_dev_reset);
5582EXPORT_SYMBOL(mptscsih_bus_reset);
5583EXPORT_SYMBOL(mptscsih_host_reset);
5584EXPORT_SYMBOL(mptscsih_bios_param);
5585EXPORT_SYMBOL(mptscsih_io_done);
5586EXPORT_SYMBOL(mptscsih_taskmgmt_complete);
5587EXPORT_SYMBOL(mptscsih_scandv_complete);
5588EXPORT_SYMBOL(mptscsih_event_process);
5589EXPORT_SYMBOL(mptscsih_ioc_reset);
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06005590EXPORT_SYMBOL(mptscsih_change_queue_depth);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005591EXPORT_SYMBOL(mptscsih_timer_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005592
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005593/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/