blob: 05832a8c5f0bbfb2d84f68b199f5c0740a35c539 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/message/fusion/mptscsih.c
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003 * For use with LSI Logic PCI chip/adapter(s)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
5 *
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04006 * Copyright (c) 1999-2005 LSI Logic Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * (mailto:mpt_linux_developer@lsil.com)
8 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
11/*
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; version 2 of the License.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 NO WARRANTY
22 THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
23 CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
24 LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
25 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
26 solely responsible for determining the appropriateness of using and
27 distributing the Program and assumes all risks associated with its
28 exercise of rights under this Agreement, including but not limited to
29 the risks and costs of program errors, damage to or loss of data,
30 programs or equipment, and unavailability or interruption of operations.
31
32 DISCLAIMER OF LIABILITY
33 NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
34 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
36 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
37 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
38 USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
39 HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
40
41 You should have received a copy of the GNU General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44*/
45/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
46
47#include "linux_compat.h" /* linux-2.6 tweaks */
48#include <linux/module.h>
49#include <linux/kernel.h>
50#include <linux/init.h>
51#include <linux/errno.h>
52#include <linux/kdev_t.h>
53#include <linux/blkdev.h>
54#include <linux/delay.h> /* for mdelay */
55#include <linux/interrupt.h> /* needed for in_interrupt() proto */
56#include <linux/reboot.h> /* notifier code */
57#include <linux/sched.h>
58#include <linux/workqueue.h>
59
60#include <scsi/scsi.h>
61#include <scsi/scsi_cmnd.h>
62#include <scsi/scsi_device.h>
63#include <scsi/scsi_host.h>
64#include <scsi/scsi_tcq.h>
Moore, Eric Deane0fc15b2005-09-15 13:17:14 -060065#include <scsi/scsi_dbg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67#include "mptbase.h"
68#include "mptscsih.h"
69
70/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
71#define my_NAME "Fusion MPT SCSI Host driver"
72#define my_VERSION MPT_LINUX_VERSION_COMMON
73#define MYNAM "mptscsih"
74
75MODULE_AUTHOR(MODULEAUTHOR);
76MODULE_DESCRIPTION(my_NAME);
77MODULE_LICENSE("GPL");
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
80
81typedef struct _BIG_SENSE_BUF {
82 u8 data[MPT_SENSE_BUFFER_ALLOC];
83} BIG_SENSE_BUF;
84
85#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */
86#define MPT_SCANDV_DID_RESET (0x00000001)
87#define MPT_SCANDV_SENSE (0x00000002)
88#define MPT_SCANDV_SOME_ERROR (0x00000004)
89#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
90#define MPT_SCANDV_ISSUE_SENSE (0x00000010)
91#define MPT_SCANDV_FALLBACK (0x00000020)
92
93#define MPT_SCANDV_MAX_RETRIES (10)
94
95#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */
96#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */
Moore, Eric Dean466544d2005-09-14 18:09:10 -060097#define MPT_ICFLAG_EBOS 0x04 /* ReadBuffer Echo buffer has EBOS */
98#define MPT_ICFLAG_PHYS_DISK 0x08 /* Any SCSI IO but do Phys Disk Format */
99#define MPT_ICFLAG_TAGGED_CMD 0x10 /* Do tagged IO */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occurred with this command */
101#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */
102
103typedef struct _internal_cmd {
104 char *data; /* data pointer */
105 dma_addr_t data_dma; /* data dma address */
106 int size; /* transfer size */
107 u8 cmd; /* SCSI Op Code */
108 u8 bus; /* bus number */
109 u8 id; /* SCSI ID (virtual) */
110 u8 lun;
111 u8 flags; /* Bit Field - See above */
112 u8 physDiskNum; /* Phys disk number, -1 else */
113 u8 rsvd2;
114 u8 rsvd;
115} INTERNAL_CMD;
116
117typedef struct _negoparms {
118 u8 width;
119 u8 offset;
120 u8 factor;
121 u8 flags;
122} NEGOPARMS;
123
124typedef struct _dv_parameters {
125 NEGOPARMS max;
126 NEGOPARMS now;
127 u8 cmd;
128 u8 id;
129 u16 pad1;
130} DVPARAMETERS;
131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132/*
133 * Other private/forward protos...
134 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400135int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136static void mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400137int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
140 SCSIIORequest_t *pReq, int req_idx);
141static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400142static 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 -0700143static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
144static int mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
145static u32 SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc);
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);
148
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400149int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
150int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700152static void mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, u8 lun, char *data, int dlen);
153static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *vtarget, char byte56);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700155static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156static 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);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700160static void mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);
161static void mptscsih_negotiate_to_asyn_narrow(MPT_SCSI_HOST *hd, VirtTarget *vtarget);
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -0700162static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
165static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io);
166static void mptscsih_domainValidation(void *hd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id);
168static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target);
169static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);
170static void mptscsih_fillbuf(char *buffer, int size, int index, int width);
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600171static void mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700172static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400175void mptscsih_remove(struct pci_dev *);
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -0700176void mptscsih_shutdown(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177#ifdef CONFIG_PM
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400178int mptscsih_suspend(struct pci_dev *pdev, pm_message_t state);
179int mptscsih_resume(struct pci_dev *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180#endif
181
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182#define SNS_LEN(scp) sizeof((scp)->sense_buffer)
183
184#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
185/*
186 * Domain Validation task structure
187 */
188static DEFINE_SPINLOCK(dvtaskQ_lock);
189static int dvtaskQ_active = 0;
190static int dvtaskQ_release = 0;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400191static struct work_struct dvTaskQ_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192#endif
193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
195/**
196 * mptscsih_add_sge - Place a simple SGE at address pAddr.
197 * @pAddr: virtual address for SGE
198 * @flagslength: SGE flags and data transfer length
199 * @dma_addr: Physical address
200 *
201 * This routine places a MPT request frame back on the MPT adapter's
202 * FreeQ.
203 */
204static inline void
205mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
206{
207 if (sizeof(dma_addr_t) == sizeof(u64)) {
208 SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
209 u32 tmp = dma_addr & 0xFFFFFFFF;
210
211 pSge->FlagsLength = cpu_to_le32(flagslength);
212 pSge->Address.Low = cpu_to_le32(tmp);
213 tmp = (u32) ((u64)dma_addr >> 32);
214 pSge->Address.High = cpu_to_le32(tmp);
215
216 } else {
217 SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
218 pSge->FlagsLength = cpu_to_le32(flagslength);
219 pSge->Address = cpu_to_le32(dma_addr);
220 }
221} /* mptscsih_add_sge() */
222
223/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
224/**
225 * mptscsih_add_chain - Place a chain SGE at address pAddr.
226 * @pAddr: virtual address for SGE
227 * @next: nextChainOffset value (u32's)
228 * @length: length of next SGL segment
229 * @dma_addr: Physical address
230 *
231 * This routine places a MPT request frame back on the MPT adapter's
232 * FreeQ.
233 */
234static inline void
235mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
236{
237 if (sizeof(dma_addr_t) == sizeof(u64)) {
238 SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
239 u32 tmp = dma_addr & 0xFFFFFFFF;
240
241 pChain->Length = cpu_to_le16(length);
242 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
243
244 pChain->NextChainOffset = next;
245
246 pChain->Address.Low = cpu_to_le32(tmp);
247 tmp = (u32) ((u64)dma_addr >> 32);
248 pChain->Address.High = cpu_to_le32(tmp);
249 } else {
250 SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
251 pChain->Length = cpu_to_le16(length);
252 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
253 pChain->NextChainOffset = next;
254 pChain->Address = cpu_to_le32(dma_addr);
255 }
256} /* mptscsih_add_chain() */
257
258/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
259/*
260 * mptscsih_getFreeChainBuffer - Function to get a free chain
261 * from the MPT_SCSI_HOST FreeChainQ.
262 * @ioc: Pointer to MPT_ADAPTER structure
263 * @req_idx: Index of the SCSI IO request frame. (output)
264 *
265 * return SUCCESS or FAILED
266 */
267static inline int
268mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex)
269{
270 MPT_FRAME_HDR *chainBuf;
271 unsigned long flags;
272 int rc;
273 int chain_idx;
274
275 dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer called\n",
276 ioc->name));
277 spin_lock_irqsave(&ioc->FreeQlock, flags);
278 if (!list_empty(&ioc->FreeChainQ)) {
279 int offset;
280
281 chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR,
282 u.frame.linkage.list);
283 list_del(&chainBuf->u.frame.linkage.list);
284 offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer;
285 chain_idx = offset / ioc->req_sz;
286 rc = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200287 dsgprintk((MYIOC_s_ERR_FMT "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n",
288 ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 } else {
290 rc = FAILED;
291 chain_idx = MPT_HOST_NO_CHAIN;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200292 dfailprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer failed\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 ioc->name));
294 }
295 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
296
297 *retIndex = chain_idx;
298 return rc;
299} /* mptscsih_getFreeChainBuffer() */
300
301/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
302/*
303 * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
304 * SCSIIORequest_t Message Frame.
305 * @ioc: Pointer to MPT_ADAPTER structure
306 * @SCpnt: Pointer to scsi_cmnd structure
307 * @pReq: Pointer to SCSIIORequest_t structure
308 *
309 * Returns ...
310 */
311static int
312mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
313 SCSIIORequest_t *pReq, int req_idx)
314{
315 char *psge;
316 char *chainSge;
317 struct scatterlist *sg;
318 int frm_sz;
319 int sges_left, sg_done;
320 int chain_idx = MPT_HOST_NO_CHAIN;
321 int sgeOffset;
322 int numSgeSlots, numSgeThisFrame;
323 u32 sgflags, sgdir, thisxfer = 0;
324 int chain_dma_off = 0;
325 int newIndex;
326 int ii;
327 dma_addr_t v2;
328 u32 RequestNB;
329
330 sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
331 if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
332 sgdir = MPT_TRANSFER_HOST_TO_IOC;
333 } else {
334 sgdir = MPT_TRANSFER_IOC_TO_HOST;
335 }
336
337 psge = (char *) &pReq->SGL;
338 frm_sz = ioc->req_sz;
339
340 /* Map the data portion, if any.
341 * sges_left = 0 if no data transfer.
342 */
343 if ( (sges_left = SCpnt->use_sg) ) {
344 sges_left = pci_map_sg(ioc->pcidev,
345 (struct scatterlist *) SCpnt->request_buffer,
346 SCpnt->use_sg,
347 SCpnt->sc_data_direction);
348 if (sges_left == 0)
349 return FAILED;
350 } else if (SCpnt->request_bufflen) {
351 SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev,
352 SCpnt->request_buffer,
353 SCpnt->request_bufflen,
354 SCpnt->sc_data_direction);
355 dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
356 ioc->name, SCpnt, SCpnt->request_bufflen));
357 mptscsih_add_sge((char *) &pReq->SGL,
358 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
359 SCpnt->SCp.dma_handle);
360
361 return SUCCESS;
362 }
363
364 /* Handle the SG case.
365 */
366 sg = (struct scatterlist *) SCpnt->request_buffer;
367 sg_done = 0;
368 sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
369 chainSge = NULL;
370
371 /* Prior to entering this loop - the following must be set
372 * current MF: sgeOffset (bytes)
373 * chainSge (Null if original MF is not a chain buffer)
374 * sg_done (num SGE done for this MF)
375 */
376
377nextSGEset:
378 numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
379 numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
380
381 sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
382
383 /* Get first (num - 1) SG elements
384 * Skip any SG entries with a length of 0
385 * NOTE: at finish, sg and psge pointed to NEXT data/location positions
386 */
387 for (ii=0; ii < (numSgeThisFrame-1); ii++) {
388 thisxfer = sg_dma_len(sg);
389 if (thisxfer == 0) {
390 sg ++; /* Get next SG element from the OS */
391 sg_done++;
392 continue;
393 }
394
395 v2 = sg_dma_address(sg);
396 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
397
398 sg++; /* Get next SG element from the OS */
399 psge += (sizeof(u32) + sizeof(dma_addr_t));
400 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
401 sg_done++;
402 }
403
404 if (numSgeThisFrame == sges_left) {
405 /* Add last element, end of buffer and end of list flags.
406 */
407 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
408 MPT_SGE_FLAGS_END_OF_BUFFER |
409 MPT_SGE_FLAGS_END_OF_LIST;
410
411 /* Add last SGE and set termination flags.
412 * Note: Last SGE may have a length of 0 - which should be ok.
413 */
414 thisxfer = sg_dma_len(sg);
415
416 v2 = sg_dma_address(sg);
417 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
418 /*
419 sg++;
420 psge += (sizeof(u32) + sizeof(dma_addr_t));
421 */
422 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
423 sg_done++;
424
425 if (chainSge) {
426 /* The current buffer is a chain buffer,
427 * but there is not another one.
428 * Update the chain element
429 * Offset and Length fields.
430 */
431 mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
432 } else {
433 /* The current buffer is the original MF
434 * and there is no Chain buffer.
435 */
436 pReq->ChainOffset = 0;
437 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200438 dsgprintk((MYIOC_s_INFO_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
440 ioc->RequestNB[req_idx] = RequestNB;
441 }
442 } else {
443 /* At least one chain buffer is needed.
444 * Complete the first MF
445 * - last SGE element, set the LastElement bit
446 * - set ChainOffset (words) for orig MF
447 * (OR finish previous MF chain buffer)
448 * - update MFStructPtr ChainIndex
449 * - Populate chain element
450 * Also
451 * Loop until done.
452 */
453
454 dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
455 ioc->name, sg_done));
456
457 /* Set LAST_ELEMENT flag for last non-chain element
458 * in the buffer. Since psge points at the NEXT
459 * SGE element, go back one SGE element, update the flags
460 * and reset the pointer. (Note: sgflags & thisxfer are already
461 * set properly).
462 */
463 if (sg_done) {
464 u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
465 sgflags = le32_to_cpu(*ptmp);
466 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
467 *ptmp = cpu_to_le32(sgflags);
468 }
469
470 if (chainSge) {
471 /* The current buffer is a chain buffer.
472 * chainSge points to the previous Chain Element.
473 * Update its chain element Offset and Length (must
474 * include chain element size) fields.
475 * Old chain element is now complete.
476 */
477 u8 nextChain = (u8) (sgeOffset >> 2);
478 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
479 mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
480 } else {
481 /* The original MF buffer requires a chain buffer -
482 * set the offset.
483 * Last element in this MF is a chain element.
484 */
485 pReq->ChainOffset = (u8) (sgeOffset >> 2);
486 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
487 dsgprintk((MYIOC_s_ERR_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
488 ioc->RequestNB[req_idx] = RequestNB;
489 }
490
491 sges_left -= sg_done;
492
493
494 /* NOTE: psge points to the beginning of the chain element
495 * in current buffer. Get a chain buffer.
496 */
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200497 if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) {
498 dfailprintk((MYIOC_s_INFO_FMT
499 "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n",
500 ioc->name, pReq->CDB[0], SCpnt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 return FAILED;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200502 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504 /* Update the tracking arrays.
505 * If chainSge == NULL, update ReqToChain, else ChainToChain
506 */
507 if (chainSge) {
508 ioc->ChainToChain[chain_idx] = newIndex;
509 } else {
510 ioc->ReqToChain[req_idx] = newIndex;
511 }
512 chain_idx = newIndex;
513 chain_dma_off = ioc->req_sz * chain_idx;
514
515 /* Populate the chainSGE for the current buffer.
516 * - Set chain buffer pointer to psge and fill
517 * out the Address and Flags fields.
518 */
519 chainSge = (char *) psge;
520 dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
521 psge, req_idx));
522
523 /* Start the SGE for the next buffer
524 */
525 psge = (char *) (ioc->ChainBuffer + chain_dma_off);
526 sgeOffset = 0;
527 sg_done = 0;
528
529 dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
530 psge, chain_idx));
531
532 /* Start the SGE for the next buffer
533 */
534
535 goto nextSGEset;
536 }
537
538 return SUCCESS;
539} /* mptscsih_AddSGE() */
540
541/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
542/*
543 * mptscsih_io_done - Main SCSI IO callback routine registered to
544 * Fusion MPT (base) driver
545 * @ioc: Pointer to MPT_ADAPTER structure
546 * @mf: Pointer to original MPT request frame
547 * @r: Pointer to MPT reply frame (NULL if TurboReply)
548 *
549 * This routine is called from mpt.c::mpt_interrupt() at the completion
550 * of any SCSI IO request.
551 * This routine is registered with the Fusion MPT (base) driver at driver
552 * load/init time via the mpt_register() API call.
553 *
554 * Returns 1 indicating alloc'd request frame ptr should be freed.
555 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400556int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
558{
559 struct scsi_cmnd *sc;
560 MPT_SCSI_HOST *hd;
561 SCSIIORequest_t *pScsiReq;
562 SCSIIOReply_t *pScsiReply;
563 u16 req_idx;
564
565 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
566
567 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
568 sc = hd->ScsiLookup[req_idx];
569 if (sc == NULL) {
570 MPIHeader_t *hdr = (MPIHeader_t *)mf;
571
572 /* Remark: writeSDP1 will use the ScsiDoneCtx
573 * If a SCSI I/O cmd, device disabled by OS and
574 * completion done. Cannot touch sc struct. Just free mem.
575 */
576 if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
577 printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n",
578 ioc->name);
579
580 mptscsih_freeChainBuffers(ioc, req_idx);
581 return 1;
582 }
583
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 sc->result = DID_OK << 16; /* Set default reply as OK */
585 pScsiReq = (SCSIIORequest_t *) mf;
586 pScsiReply = (SCSIIOReply_t *) mr;
587
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200588 if((ioc->facts.MsgVersion >= MPI_VERSION_01_05) && pScsiReply){
589 dmfprintk((MYIOC_s_INFO_FMT
590 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d,task-tag=%d)\n",
591 ioc->name, mf, mr, sc, req_idx, pScsiReply->TaskTag));
592 }else{
593 dmfprintk((MYIOC_s_INFO_FMT
594 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n",
595 ioc->name, mf, mr, sc, req_idx));
596 }
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 if (pScsiReply == NULL) {
599 /* special context reply handling */
600 ;
601 } else {
602 u32 xfer_cnt;
603 u16 status;
604 u8 scsi_state, scsi_status;
605
606 status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
607 scsi_state = pScsiReply->SCSIState;
608 scsi_status = pScsiReply->SCSIStatus;
609 xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
610 sc->resid = sc->request_bufflen - xfer_cnt;
611
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600612 /*
613 * if we get a data underrun indication, yet no data was
614 * transferred and the SCSI status indicates that the
615 * command was never started, change the data underrun
616 * to success
617 */
618 if (status == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
619 (scsi_status == MPI_SCSI_STATUS_BUSY ||
620 scsi_status == MPI_SCSI_STATUS_RESERVATION_CONFLICT ||
621 scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)) {
622 status = MPI_IOCSTATUS_SUCCESS;
623 }
624
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n"
626 "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n"
627 "resid=%d bufflen=%d xfer_cnt=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700628 ioc->id, sc->device->id, sc->device->lun,
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600629 status, scsi_state, scsi_status, sc->resid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 sc->request_bufflen, xfer_cnt));
631
632 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400633 mptscsih_copy_sense_data(sc, hd, mf, pScsiReply);
634
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 /*
636 * Look for + dump FCP ResponseInfo[]!
637 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600638 if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID &&
639 pScsiReply->ResponseInfo) {
640 printk(KERN_NOTICE "ha=%d id=%d lun=%d: "
641 "FCP_ResponseInfo=%08xh\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700642 ioc->id, sc->device->id, sc->device->lun,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 le32_to_cpu(pScsiReply->ResponseInfo));
644 }
645
646 switch(status) {
647 case MPI_IOCSTATUS_BUSY: /* 0x0002 */
648 /* CHECKME!
649 * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
650 * But not: DID_BUS_BUSY lest one risk
651 * killing interrupt handler:-(
652 */
653 sc->result = SAM_STAT_BUSY;
654 break;
655
656 case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
657 case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
658 sc->result = DID_BAD_TARGET << 16;
659 break;
660
661 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
662 /* Spoof to SCSI Selection Timeout! */
663 sc->result = DID_NO_CONNECT << 16;
664
665 if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF)
666 hd->sel_timeout[pScsiReq->TargetID]++;
667 break;
668
669 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
670 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
671 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
672 /* Linux handles an unsolicited DID_RESET better
673 * than an unsolicited DID_ABORT.
674 */
675 sc->result = DID_RESET << 16;
676
677 /* GEM Workaround. */
Moore, Eric Deana9b29372005-11-16 18:54:20 -0700678 if (ioc->bus_type == SPI)
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700679 mptscsih_no_negotiate(hd, sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 break;
681
682 case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600683 sc->resid = sc->request_bufflen - xfer_cnt;
684 if((xfer_cnt==0)||(sc->underflow > xfer_cnt))
685 sc->result=DID_SOFT_ERROR << 16;
686 else /* Sufficient data transfer occurred */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 sc->result = (DID_OK << 16) | scsi_status;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600688 dreplyprintk((KERN_NOTICE
689 "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->device->id));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 break;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400691
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
693 /*
694 * Do upfront check for valid SenseData and give it
695 * precedence!
696 */
697 sc->result = (DID_OK << 16) | scsi_status;
698 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
699 /* Have already saved the status and sense data
700 */
701 ;
702 } else {
703 if (xfer_cnt < sc->underflow) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600704 if (scsi_status == SAM_STAT_BUSY)
705 sc->result = SAM_STAT_BUSY;
706 else
707 sc->result = DID_SOFT_ERROR << 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
709 if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
710 /* What to do?
711 */
712 sc->result = DID_SOFT_ERROR << 16;
713 }
714 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
715 /* Not real sure here either... */
716 sc->result = DID_RESET << 16;
717 }
718 }
719
720 dreplyprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n",
721 sc->underflow));
722 dreplyprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt));
723 /* Report Queue Full
724 */
725 if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)
726 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400727
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 break;
729
730 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
731 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600732 if (scsi_status == MPI_SCSI_STATUS_BUSY)
733 sc->result = (DID_BUS_BUSY << 16) | scsi_status;
734 else
735 sc->result = (DID_OK << 16) | scsi_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 if (scsi_state == 0) {
737 ;
738 } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
739 /*
740 * If running against circa 200003dd 909 MPT f/w,
741 * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
742 * (QUEUE_FULL) returned from device! --> get 0x0000?128
743 * and with SenseBytes set to 0.
744 */
745 if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
746 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
747
748 }
749 else if (scsi_state &
750 (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
751 ) {
752 /*
753 * What to do?
754 */
755 sc->result = DID_SOFT_ERROR << 16;
756 }
757 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
758 /* Not real sure here either... */
759 sc->result = DID_RESET << 16;
760 }
761 else if (scsi_state & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) {
762 /* Device Inq. data indicates that it supports
763 * QTags, but rejects QTag messages.
764 * This command completed OK.
765 *
766 * Not real sure here either so do nothing... */
767 }
768
769 if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
770 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
771
772 /* Add handling of:
773 * Reservation Conflict, Busy,
774 * Command Terminated, CHECK
775 */
776 break;
777
778 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
779 sc->result = DID_SOFT_ERROR << 16;
780 break;
781
782 case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
783 case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
784 case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
785 case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
786 case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
787 case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
788 case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
789 case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
790 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
791 case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
792 default:
793 /*
794 * What to do?
795 */
796 sc->result = DID_SOFT_ERROR << 16;
797 break;
798
799 } /* switch(status) */
800
801 dreplyprintk((KERN_NOTICE " sc->result is %08xh\n", sc->result));
802 } /* end of address reply case */
803
804 /* Unmap the DMA buffers, if any. */
805 if (sc->use_sg) {
806 pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
807 sc->use_sg, sc->sc_data_direction);
808 } else if (sc->request_bufflen) {
809 pci_unmap_single(ioc->pcidev, sc->SCp.dma_handle,
810 sc->request_bufflen, sc->sc_data_direction);
811 }
812
813 hd->ScsiLookup[req_idx] = NULL;
814
815 sc->scsi_done(sc); /* Issue the command callback */
816
817 /* Free Chain buffers */
818 mptscsih_freeChainBuffers(ioc, req_idx);
819 return 1;
820}
821
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822/*
823 * mptscsih_flush_running_cmds - For each command found, search
824 * Scsi_Host instance taskQ and reply to OS.
825 * Called only if recovering from a FW reload.
826 * @hd: Pointer to a SCSI HOST structure
827 *
828 * Returns: None.
829 *
830 * Must be called while new I/Os are being queued.
831 */
832static void
833mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
834{
835 MPT_ADAPTER *ioc = hd->ioc;
836 struct scsi_cmnd *SCpnt;
837 MPT_FRAME_HDR *mf;
838 int ii;
839 int max = ioc->req_depth;
840
841 dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n"));
842 for (ii= 0; ii < max; ii++) {
843 if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
844
845 /* Command found.
846 */
847
848 /* Null ScsiLookup index
849 */
850 hd->ScsiLookup[ii] = NULL;
851
852 mf = MPT_INDEX_2_MFPTR(ioc, ii);
853 dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n",
854 mf, SCpnt));
855
856 /* Set status, free OS resources (SG DMA buffers)
857 * Do OS callback
858 * Free driver resources (chain, msg buffers)
859 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400860 if (SCpnt->use_sg) {
861 pci_unmap_sg(ioc->pcidev,
862 (struct scatterlist *) SCpnt->request_buffer,
863 SCpnt->use_sg,
864 SCpnt->sc_data_direction);
865 } else if (SCpnt->request_bufflen) {
866 pci_unmap_single(ioc->pcidev,
867 SCpnt->SCp.dma_handle,
868 SCpnt->request_bufflen,
869 SCpnt->sc_data_direction);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 }
871 SCpnt->result = DID_RESET << 16;
872 SCpnt->host_scribble = NULL;
873
874 /* Free Chain buffers */
875 mptscsih_freeChainBuffers(ioc, ii);
876
877 /* Free Message frames */
878 mpt_free_msg_frame(ioc, mf);
879
880 SCpnt->scsi_done(SCpnt); /* Issue the command callback */
881 }
882 }
883
884 return;
885}
886
887/*
888 * mptscsih_search_running_cmds - Delete any commands associated
889 * with the specified target and lun. Function called only
890 * when a lun is disable by mid-layer.
891 * Do NOT access the referenced scsi_cmnd structure or
892 * members. Will cause either a paging or NULL ptr error.
Michael Reed05e8ec12006-01-13 14:31:54 -0600893 * (BUT, BUT, BUT, the code does reference it! - mdr)
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700894 * @hd: Pointer to a SCSI HOST structure
895 * @vdevice: per device private data
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 *
897 * Returns: None.
898 *
899 * Called from slave_destroy.
900 */
901static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700902mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903{
904 SCSIIORequest_t *mf = NULL;
905 int ii;
906 int max = hd->ioc->req_depth;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600907 struct scsi_cmnd *sc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
909 dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700910 vdevice->target_id, vdevice->lun, max));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 for (ii=0; ii < max; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600913 if ((sc = hd->ScsiLookup[ii]) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
915 mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(hd->ioc, ii);
916
917 dsprintk(( "search_running: found (sc=%p, mf = %p) target %d, lun %d \n",
918 hd->ScsiLookup[ii], mf, mf->TargetID, mf->LUN[1]));
919
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700920 if ((mf->TargetID != ((u8)vdevice->target_id)) || (mf->LUN[1] != ((u8) vdevice->lun)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 continue;
922
923 /* Cleanup
924 */
925 hd->ScsiLookup[ii] = NULL;
926 mptscsih_freeChainBuffers(hd->ioc, ii);
927 mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf);
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600928 if (sc->use_sg) {
929 pci_unmap_sg(hd->ioc->pcidev,
930 (struct scatterlist *) sc->request_buffer,
931 sc->use_sg,
932 sc->sc_data_direction);
933 } else if (sc->request_bufflen) {
934 pci_unmap_single(hd->ioc->pcidev,
935 sc->SCp.dma_handle,
936 sc->request_bufflen,
937 sc->sc_data_direction);
938 }
939 sc->host_scribble = NULL;
940 sc->result = DID_NO_CONNECT << 16;
941 sc->scsi_done(sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 }
943 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 return;
945}
946
947/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
950/*
951 * mptscsih_report_queue_full - Report QUEUE_FULL status returned
952 * from a SCSI target device.
953 * @sc: Pointer to scsi_cmnd structure
954 * @pScsiReply: Pointer to SCSIIOReply_t
955 * @pScsiReq: Pointer to original SCSI request
956 *
957 * This routine periodically reports QUEUE_FULL status returned from a
958 * SCSI target device. It reports this to the console via kernel
959 * printk() API call, not more than once every 10 seconds.
960 */
961static void
962mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
963{
964 long time = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 MPT_SCSI_HOST *hd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400967 if (sc->device == NULL)
968 return;
969 if (sc->device->host == NULL)
970 return;
971 if ((hd = (MPT_SCSI_HOST *)sc->device->host->hostdata) == NULL)
972 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400974 if (time - hd->last_queue_full > 10 * HZ) {
975 dprintk((MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n",
976 hd->ioc->name, 0, sc->device->id, sc->device->lun));
977 hd->last_queue_full = time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979}
980
981/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
982/*
983 * mptscsih_remove - Removed scsi devices
984 * @pdev: Pointer to pci_dev structure
985 *
986 *
987 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400988void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989mptscsih_remove(struct pci_dev *pdev)
990{
991 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
992 struct Scsi_Host *host = ioc->sh;
993 MPT_SCSI_HOST *hd;
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -0700994#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 int count;
996 unsigned long flags;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700997#endif
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400998 int sz1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001000 if(!host) {
1001 mpt_detach(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 return;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001003 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
1005 scsi_remove_host(host);
1006
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001007 if((hd = (MPT_SCSI_HOST *)host->hostdata) == NULL)
1008 return;
1009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1011 /* Check DV thread active */
1012 count = 10 * HZ;
1013 spin_lock_irqsave(&dvtaskQ_lock, flags);
1014 if (dvtaskQ_active) {
1015 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
Nishanth Aravamudan65210182005-11-07 01:01:19 -08001016 while(dvtaskQ_active && --count)
1017 schedule_timeout_interruptible(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 } else {
1019 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
1020 }
1021 if (!count)
1022 printk(KERN_ERR MYNAM ": ERROR - DV thread still active!\n");
1023#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
1024 else
1025 printk(KERN_ERR MYNAM ": DV thread orig %d, count %d\n", 10 * HZ, count);
1026#endif
1027#endif
1028
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001029 mptscsih_shutdown(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001031 sz1=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001033 if (hd->ScsiLookup != NULL) {
1034 sz1 = hd->ioc->req_depth * sizeof(void *);
1035 kfree(hd->ScsiLookup);
1036 hd->ScsiLookup = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 }
1038
Moore, Eric Dean d485eb82005-05-11 17:37:26 -06001039 /*
1040 * Free pointer array.
1041 */
1042 kfree(hd->Targets);
1043 hd->Targets = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001045 dprintk((MYIOC_s_INFO_FMT
1046 "Free'd ScsiLookup (%d) memory\n",
1047 hd->ioc->name, sz1));
1048
Moore, Eric Dean d485eb82005-05-11 17:37:26 -06001049 kfree(hd->info_kbuf);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001050
1051 /* NULL the Scsi_Host pointer
1052 */
1053 hd->ioc->sh = NULL;
1054
1055 scsi_host_put(host);
1056
1057 mpt_detach(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059}
1060
1061/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1062/*
1063 * mptscsih_shutdown - reboot notifier
1064 *
1065 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001066void
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001067mptscsih_shutdown(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001069 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 struct Scsi_Host *host = ioc->sh;
1071 MPT_SCSI_HOST *hd;
1072
1073 if(!host)
1074 return;
1075
1076 hd = (MPT_SCSI_HOST *)host->hostdata;
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078}
1079
1080#ifdef CONFIG_PM
1081/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1082/*
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001083 * mptscsih_suspend - Fusion MPT scsi driver suspend routine.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 *
1085 *
1086 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001087int
Pavel Machek8d189f72005-04-16 15:25:28 -07001088mptscsih_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001090 mptscsih_shutdown(pdev);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001091 return mpt_suspend(pdev,state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092}
1093
1094/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1095/*
1096 * mptscsih_resume - Fusion MPT scsi driver resume routine.
1097 *
1098 *
1099 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001100int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101mptscsih_resume(struct pci_dev *pdev)
1102{
1103 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
1104 struct Scsi_Host *host = ioc->sh;
1105 MPT_SCSI_HOST *hd;
1106
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001107 mpt_resume(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001108
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 if(!host)
1110 return 0;
1111
1112 hd = (MPT_SCSI_HOST *)host->hostdata;
1113 if(!hd)
1114 return 0;
1115
1116#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1117 {
1118 unsigned long lflags;
1119 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1120 if (!dvtaskQ_active) {
1121 dvtaskQ_active = 1;
1122 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001123 INIT_WORK(&dvTaskQ_task,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 mptscsih_domainValidation, (void *) hd);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001125 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 } else {
1127 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1128 }
1129 }
1130#endif
1131 return 0;
1132}
1133
1134#endif
1135
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1137/**
1138 * mptscsih_info - Return information about MPT adapter
1139 * @SChost: Pointer to Scsi_Host structure
1140 *
1141 * (linux scsi_host_template.info routine)
1142 *
1143 * Returns pointer to buffer where information was written.
1144 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001145const char *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146mptscsih_info(struct Scsi_Host *SChost)
1147{
1148 MPT_SCSI_HOST *h;
1149 int size = 0;
1150
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 h = (MPT_SCSI_HOST *)SChost->hostdata;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001152
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 if (h) {
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001154 if (h->info_kbuf == NULL)
1155 if ((h->info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
1156 return h->info_kbuf;
1157 h->info_kbuf[0] = '\0';
1158
1159 mpt_print_ioc_summary(h->ioc, h->info_kbuf, &size, 0, 0);
1160 h->info_kbuf[size-1] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 }
1162
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001163 return h->info_kbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164}
1165
1166struct info_str {
1167 char *buffer;
1168 int length;
1169 int offset;
1170 int pos;
1171};
1172
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001173static void
1174mptscsih_copy_mem_info(struct info_str *info, char *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175{
1176 if (info->pos + len > info->length)
1177 len = info->length - info->pos;
1178
1179 if (info->pos + len < info->offset) {
1180 info->pos += len;
1181 return;
1182 }
1183
1184 if (info->pos < info->offset) {
1185 data += (info->offset - info->pos);
1186 len -= (info->offset - info->pos);
1187 }
1188
1189 if (len > 0) {
1190 memcpy(info->buffer + info->pos, data, len);
1191 info->pos += len;
1192 }
1193}
1194
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001195static int
1196mptscsih_copy_info(struct info_str *info, char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197{
1198 va_list args;
1199 char buf[81];
1200 int len;
1201
1202 va_start(args, fmt);
1203 len = vsprintf(buf, fmt, args);
1204 va_end(args);
1205
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001206 mptscsih_copy_mem_info(info, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 return len;
1208}
1209
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001210static int
1211mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212{
1213 struct info_str info;
1214
1215 info.buffer = pbuf;
1216 info.length = len;
1217 info.offset = offset;
1218 info.pos = 0;
1219
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001220 mptscsih_copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name);
1221 mptscsih_copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word);
1222 mptscsih_copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts);
1223 mptscsih_copy_info(&info, "MaxQ=%d\n", ioc->req_depth);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224
1225 return ((info.pos > info.offset) ? info.pos - info.offset : 0);
1226}
1227
1228/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1229/**
1230 * mptscsih_proc_info - Return information about MPT adapter
1231 *
1232 * (linux scsi_host_template.info routine)
1233 *
1234 * buffer: if write, user data; if read, buffer for user
1235 * length: if write, return length;
1236 * offset: if write, 0; if read, the current offset into the buffer from
1237 * the previous read.
1238 * hostno: scsi host number
1239 * func: if write = 1; if read = 0
1240 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001241int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
1243 int length, int func)
1244{
1245 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
1246 MPT_ADAPTER *ioc = hd->ioc;
1247 int size = 0;
1248
1249 if (func) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001250 /*
1251 * write is not supported
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 */
1253 } else {
1254 if (start)
1255 *start = buffer;
1256
1257 size = mptscsih_host_info(ioc, buffer, offset, length);
1258 }
1259
1260 return size;
1261}
1262
1263/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1264#define ADD_INDEX_LOG(req_ent) do { } while(0)
1265
1266/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1267/**
1268 * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
1269 * @SCpnt: Pointer to scsi_cmnd structure
1270 * @done: Pointer SCSI mid-layer IO completion function
1271 *
1272 * (linux scsi_host_template.queuecommand routine)
1273 * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
1274 * from a linux scsi_cmnd request and send it to the IOC.
1275 *
1276 * Returns 0. (rtn value discarded by linux scsi mid-layer)
1277 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001278int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
1280{
1281 MPT_SCSI_HOST *hd;
1282 MPT_FRAME_HDR *mf;
1283 SCSIIORequest_t *pScsiReq;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001284 VirtDevice *vdev = SCpnt->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 int lun;
1286 u32 datalen;
1287 u32 scsictl;
1288 u32 scsidir;
1289 u32 cmd_len;
1290 int my_idx;
1291 int ii;
1292
1293 hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 lun = SCpnt->device->lun;
1295 SCpnt->scsi_done = done;
1296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
1298 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
1299
1300 if (hd->resetPending) {
1301 dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
1302 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
1303 return SCSI_MLQUEUE_HOST_BUSY;
1304 }
1305
1306 /*
1307 * Put together a MPT SCSI request...
1308 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001309 if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
1311 hd->ioc->name));
1312 return SCSI_MLQUEUE_HOST_BUSY;
1313 }
1314
1315 pScsiReq = (SCSIIORequest_t *) mf;
1316
1317 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
1318
1319 ADD_INDEX_LOG(my_idx);
1320
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001321 /* TUR's being issued with scsictl=0x02000000 (DATA_IN)!
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 * Seems we may receive a buffer (datalen>0) even when there
1323 * will be no data transfer! GRRRRR...
1324 */
1325 if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
1326 datalen = SCpnt->request_bufflen;
1327 scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
1328 } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
1329 datalen = SCpnt->request_bufflen;
1330 scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
1331 } else {
1332 datalen = 0;
1333 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
1334 }
1335
1336 /* Default to untagged. Once a target structure has been allocated,
1337 * use the Inquiry data to determine if device supports tagged.
1338 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001339 if (vdev
1340 && (vdev->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 && (SCpnt->device->tagged_supported)) {
1342 scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
1343 } else {
1344 scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
1345 }
1346
1347 /* Use the above information to set up the message frame
1348 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001349 pScsiReq->TargetID = (u8) vdev->target_id;
1350 pScsiReq->Bus = vdev->bus_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 pScsiReq->ChainOffset = 0;
1352 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
1353 pScsiReq->CDBLength = SCpnt->cmd_len;
1354 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
1355 pScsiReq->Reserved = 0;
1356 pScsiReq->MsgFlags = mpt_msg_flags();
1357 pScsiReq->LUN[0] = 0;
1358 pScsiReq->LUN[1] = lun;
1359 pScsiReq->LUN[2] = 0;
1360 pScsiReq->LUN[3] = 0;
1361 pScsiReq->LUN[4] = 0;
1362 pScsiReq->LUN[5] = 0;
1363 pScsiReq->LUN[6] = 0;
1364 pScsiReq->LUN[7] = 0;
1365 pScsiReq->Control = cpu_to_le32(scsictl);
1366
1367 /*
1368 * Write SCSI CDB into the message
1369 */
1370 cmd_len = SCpnt->cmd_len;
1371 for (ii=0; ii < cmd_len; ii++)
1372 pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
1373
1374 for (ii=cmd_len; ii < 16; ii++)
1375 pScsiReq->CDB[ii] = 0;
1376
1377 /* DataLength */
1378 pScsiReq->DataLength = cpu_to_le32(datalen);
1379
1380 /* SenseBuffer low address */
1381 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
1382 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
1383
1384 /* Now add the SG list
1385 * Always have a SGE even if null length.
1386 */
1387 if (datalen == 0) {
1388 /* Add a NULL SGE */
1389 mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
1390 (dma_addr_t) -1);
1391 } else {
1392 /* Add a 32 or 64 bit SGE */
1393 if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS)
1394 goto fail;
1395 }
1396
1397 hd->ScsiLookup[my_idx] = SCpnt;
1398 SCpnt->host_scribble = NULL;
1399
1400#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
Moore, Eric Deana9b29372005-11-16 18:54:20 -07001401 if (hd->ioc->bus_type == SPI) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001402 int dvStatus = hd->ioc->spi_data.dvStatus[vdev->target_id];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 int issueCmd = 1;
1404
1405 if (dvStatus || hd->ioc->spi_data.forceDv) {
1406
1407 if ((dvStatus & MPT_SCSICFG_NEED_DV) ||
1408 (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) {
1409 unsigned long lflags;
1410 /* Schedule DV if necessary */
1411 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1412 if (!dvtaskQ_active) {
1413 dvtaskQ_active = 1;
1414 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001415 INIT_WORK(&dvTaskQ_task, mptscsih_domainValidation, (void *) hd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001417 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 } else {
1419 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1420 }
1421 hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV;
1422 }
1423
1424 /* Trying to do DV to this target, extend timeout.
1425 * Wait to issue until flag is clear
1426 */
1427 if (dvStatus & MPT_SCSICFG_DV_PENDING) {
1428 mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
1429 issueCmd = 0;
1430 }
1431
1432 /* Set the DV flags.
1433 */
1434 if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001435 mptscsih_set_dvflags(hd, SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
1437 if (!issueCmd)
1438 goto fail;
1439 }
1440 }
1441#endif
1442
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001443 mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
1445 hd->ioc->name, SCpnt, mf, my_idx));
1446 DBG_DUMP_REQUEST_FRAME(mf)
1447 return 0;
1448
1449 fail:
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001450 hd->ScsiLookup[my_idx] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 mptscsih_freeChainBuffers(hd->ioc, my_idx);
1452 mpt_free_msg_frame(hd->ioc, mf);
1453 return SCSI_MLQUEUE_HOST_BUSY;
1454}
1455
1456/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1457/*
1458 * mptscsih_freeChainBuffers - Function to free chain buffers associated
1459 * with a SCSI IO request
1460 * @hd: Pointer to the MPT_SCSI_HOST instance
1461 * @req_idx: Index of the SCSI IO request frame.
1462 *
1463 * Called if SG chain buffer allocation fails and mptscsih callbacks.
1464 * No return.
1465 */
1466static void
1467mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
1468{
1469 MPT_FRAME_HDR *chain;
1470 unsigned long flags;
1471 int chain_idx;
1472 int next;
1473
1474 /* Get the first chain index and reset
1475 * tracker state.
1476 */
1477 chain_idx = ioc->ReqToChain[req_idx];
1478 ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
1479
1480 while (chain_idx != MPT_HOST_NO_CHAIN) {
1481
1482 /* Save the next chain buffer index */
1483 next = ioc->ChainToChain[chain_idx];
1484
1485 /* Free this chain buffer and reset
1486 * tracker
1487 */
1488 ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
1489
1490 chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer
1491 + (chain_idx * ioc->req_sz));
1492
1493 spin_lock_irqsave(&ioc->FreeQlock, flags);
1494 list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ);
1495 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
1496
1497 dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
1498 ioc->name, chain_idx));
1499
1500 /* handle next */
1501 chain_idx = next;
1502 }
1503 return;
1504}
1505
1506/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1507/*
1508 * Reset Handling
1509 */
1510
1511/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1512/*
1513 * mptscsih_TMHandler - Generic handler for SCSI Task Management.
1514 * Fall through to mpt_HardResetHandler if: not operational, too many
1515 * failed TM requests or handshake failure.
1516 *
1517 * @ioc: Pointer to MPT_ADAPTER structure
1518 * @type: Task Management type
1519 * @target: Logical Target ID for reset (if appropriate)
1520 * @lun: Logical Unit for reset (if appropriate)
1521 * @ctx2abort: Context for the task to be aborted (if appropriate)
1522 *
1523 * Remark: Currently invoked from a non-interrupt thread (_bh).
1524 *
1525 * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
1526 * will be active.
1527 *
1528 * Returns 0 for SUCCESS or -1 if FAILED.
1529 */
James Bottomley663e1aa2006-01-29 12:10:24 -06001530int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1532{
1533 MPT_ADAPTER *ioc;
1534 int rc = -1;
1535 int doTask = 1;
1536 u32 ioc_raw_state;
1537 unsigned long flags;
1538
1539 /* If FW is being reloaded currently, return success to
1540 * the calling function.
1541 */
1542 if (hd == NULL)
1543 return 0;
1544
1545 ioc = hd->ioc;
1546 if (ioc == NULL) {
1547 printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
1548 return FAILED;
1549 }
1550 dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
1551
1552 // SJR - CHECKME - Can we avoid this here?
1553 // (mpt_HardResetHandler has this check...)
1554 spin_lock_irqsave(&ioc->diagLock, flags);
1555 if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
1556 spin_unlock_irqrestore(&ioc->diagLock, flags);
1557 return FAILED;
1558 }
1559 spin_unlock_irqrestore(&ioc->diagLock, flags);
1560
1561 /* Wait a fixed amount of time for the TM pending flag to be cleared.
1562 * If we time out and not bus reset, then we return a FAILED status to the caller.
1563 * The call to mptscsih_tm_pending_wait() will set the pending flag if we are
1564 * successful. Otherwise, reload the FW.
1565 */
1566 if (mptscsih_tm_pending_wait(hd) == FAILED) {
1567 if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001568 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 "Timed out waiting for last TM (%d) to complete! \n",
1570 hd->ioc->name, hd->tmPending));
1571 return FAILED;
1572 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001573 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 "Timed out waiting for last TM (%d) to complete! \n",
1575 hd->ioc->name, hd->tmPending));
1576 return FAILED;
1577 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001578 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 "Timed out waiting for last TM (%d) to complete! \n",
1580 hd->ioc->name, hd->tmPending));
1581 if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
1582 return FAILED;
1583
1584 doTask = 0;
1585 }
1586 } else {
1587 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1588 hd->tmPending |= (1 << type);
1589 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1590 }
1591
1592 /* Is operational?
1593 */
1594 ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
1595
1596#ifdef MPT_DEBUG_RESET
1597 if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
1598 printk(MYIOC_s_WARN_FMT
1599 "TM Handler: IOC Not operational(0x%x)!\n",
1600 hd->ioc->name, ioc_raw_state);
1601 }
1602#endif
1603
1604 if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
1605 && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
1606
1607 /* Isse the Task Mgmt request.
1608 */
1609 if (hd->hard_resets < -1)
1610 hd->hard_resets++;
1611 rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout);
1612 if (rc) {
1613 printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
1614 } else {
1615 dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
1616 }
1617 }
1618
1619 /* Only fall through to the HRH if this is a bus reset
1620 */
1621 if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
1622 ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
1623 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1624 hd->ioc->name));
1625 rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1626 }
1627
1628 dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
1629
1630 return rc;
1631}
1632
1633
1634/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1635/*
1636 * mptscsih_IssueTaskMgmt - Generic send Task Management function.
1637 * @hd: Pointer to MPT_SCSI_HOST structure
1638 * @type: Task Management type
1639 * @target: Logical Target ID for reset (if appropriate)
1640 * @lun: Logical Unit for reset (if appropriate)
1641 * @ctx2abort: Context for the task to be aborted (if appropriate)
1642 *
1643 * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
1644 * or a non-interrupt thread. In the former, must not call schedule().
1645 *
1646 * Not all fields are meaningfull for all task types.
1647 *
1648 * Returns 0 for SUCCESS, -999 for "no msg frames",
1649 * else other non-zero value returned.
1650 */
1651static int
1652mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1653{
1654 MPT_FRAME_HDR *mf;
1655 SCSITaskMgmt_t *pScsiTm;
1656 int ii;
1657 int retval;
1658
1659 /* Return Fail to calling function if no message frames available.
1660 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001661 if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
1663 hd->ioc->name));
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001664 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 }
1666 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
1667 hd->ioc->name, mf));
1668
1669 /* Format the Request
1670 */
1671 pScsiTm = (SCSITaskMgmt_t *) mf;
1672 pScsiTm->TargetID = target;
1673 pScsiTm->Bus = channel;
1674 pScsiTm->ChainOffset = 0;
1675 pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1676
1677 pScsiTm->Reserved = 0;
1678 pScsiTm->TaskType = type;
1679 pScsiTm->Reserved1 = 0;
1680 pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
1681 ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
1682
1683 for (ii= 0; ii < 8; ii++) {
1684 pScsiTm->LUN[ii] = 0;
1685 }
1686 pScsiTm->LUN[1] = lun;
1687
1688 for (ii=0; ii < 7; ii++)
1689 pScsiTm->Reserved2[ii] = 0;
1690
1691 pScsiTm->TaskMsgContext = ctx2abort;
1692
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001693 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n",
1694 hd->ioc->name, ctx2abort, type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm);
1697
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001698 if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 sizeof(SCSITaskMgmt_t), (u32*)pScsiTm,
1700 CAN_SLEEP)) != 0) {
1701 dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!"
1702 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1703 hd->ioc, mf));
1704 mpt_free_msg_frame(hd->ioc, mf);
1705 return retval;
1706 }
1707
1708 if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
1709 dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!"
1710 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1711 hd->ioc, mf));
1712 mpt_free_msg_frame(hd->ioc, mf);
1713 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1714 hd->ioc->name));
1715 retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1716 }
1717
1718 return retval;
1719}
1720
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001721static int
1722mptscsih_get_tm_timeout(MPT_ADAPTER *ioc)
1723{
1724 switch (ioc->bus_type) {
1725 case FC:
1726 return 40;
1727 case SAS:
1728 return 10;
1729 case SPI:
1730 default:
1731 return 2;
1732 }
1733}
1734
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1736/**
1737 * mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant
1738 * @SCpnt: Pointer to scsi_cmnd structure, IO to be aborted
1739 *
1740 * (linux scsi_host_template.eh_abort_handler routine)
1741 *
1742 * Returns SUCCESS or FAILED.
1743 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001744int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745mptscsih_abort(struct scsi_cmnd * SCpnt)
1746{
1747 MPT_SCSI_HOST *hd;
1748 MPT_ADAPTER *ioc;
1749 MPT_FRAME_HDR *mf;
1750 u32 ctx2abort;
1751 int scpnt_idx;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001752 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001753 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754
1755 /* If we can't locate our host adapter structure, return FAILED status.
1756 */
1757 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) {
1758 SCpnt->result = DID_RESET << 16;
1759 SCpnt->scsi_done(SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001760 dfailprintk((KERN_INFO MYNAM ": mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 "Can't locate host! (sc=%p)\n",
1762 SCpnt));
1763 return FAILED;
1764 }
1765
1766 ioc = hd->ioc;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001767 if (hd->resetPending) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 return FAILED;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001769 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
1771 if (hd->timeouts < -1)
1772 hd->timeouts++;
1773
1774 /* Find this command
1775 */
1776 if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001777 /* Cmd not found in ScsiLookup.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 * Do OS callback.
1779 */
1780 SCpnt->result = DID_RESET << 16;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001781 dtmprintk((KERN_INFO MYNAM ": %s: mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 "Command not in the active list! (sc=%p)\n",
1783 hd->ioc->name, SCpnt));
1784 return SUCCESS;
1785 }
1786
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001787 printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n",
1788 hd->ioc->name, SCpnt);
1789 scsi_print_command(SCpnt);
1790
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
1792 * (the IO to be ABORT'd)
1793 *
1794 * NOTE: Since we do not byteswap MsgContext, we do not
1795 * swap it here either. It is an opaque cookie to
1796 * the controller, so it does not matter. -DaveM
1797 */
1798 mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
1799 ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
1800
1801 hd->abortSCpnt = SCpnt;
1802
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001803 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001804 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001805 vdev->bus_id, vdev->target_id, vdev->lun,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001806 ctx2abort, mptscsih_get_tm_timeout(ioc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001808 printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n",
1809 hd->ioc->name,
1810 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001812 if (retval == 0)
1813 return SUCCESS;
1814
1815 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 hd->tmPending = 0;
1817 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001819 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820}
1821
1822/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1823/**
1824 * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
1825 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1826 *
1827 * (linux scsi_host_template.eh_dev_reset_handler routine)
1828 *
1829 * Returns SUCCESS or FAILED.
1830 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001831int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
1833{
1834 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001835 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001836 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
1838 /* If we can't locate our host adapter structure, return FAILED status.
1839 */
1840 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001841 dtmprintk((KERN_INFO MYNAM ": mptscsih_dev_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 "Can't locate host! (sc=%p)\n",
1843 SCpnt));
1844 return FAILED;
1845 }
1846
1847 if (hd->resetPending)
1848 return FAILED;
1849
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001850 printk(KERN_WARNING MYNAM ": %s: attempting target reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001852 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001854 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001855 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001856 vdev->bus_id, vdev->target_id,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001857 0, 0, mptscsih_get_tm_timeout(hd->ioc));
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001858
1859 printk (KERN_WARNING MYNAM ": %s: target reset: %s (sc=%p)\n",
1860 hd->ioc->name,
1861 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1862
1863 if (retval == 0)
1864 return SUCCESS;
1865
1866 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 hd->tmPending = 0;
1868 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001870 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871}
1872
1873/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1874/**
1875 * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
1876 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1877 *
1878 * (linux scsi_host_template.eh_bus_reset_handler routine)
1879 *
1880 * Returns SUCCESS or FAILED.
1881 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001882int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
1884{
1885 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001886 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001887 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888
1889 /* If we can't locate our host adapter structure, return FAILED status.
1890 */
1891 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001892 dtmprintk((KERN_INFO MYNAM ": mptscsih_bus_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 "Can't locate host! (sc=%p)\n",
1894 SCpnt ) );
1895 return FAILED;
1896 }
1897
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001898 printk(KERN_WARNING MYNAM ": %s: attempting bus reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001900 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901
1902 if (hd->timeouts < -1)
1903 hd->timeouts++;
1904
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001905 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001906 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
Christoph Hellwigd66c7a02006-01-17 13:43:14 +00001907 vdev->bus_id, 0, 0, 0, mptscsih_get_tm_timeout(hd->ioc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001909 printk (KERN_WARNING MYNAM ": %s: bus reset: %s (sc=%p)\n",
1910 hd->ioc->name,
1911 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1912
1913 if (retval == 0)
1914 return SUCCESS;
1915
1916 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 hd->tmPending = 0;
1918 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001920 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921}
1922
1923/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1924/**
1925 * mptscsih_host_reset - Perform a SCSI host adapter RESET!
1926 * new_eh variant
1927 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1928 *
1929 * (linux scsi_host_template.eh_host_reset_handler routine)
1930 *
1931 * Returns SUCCESS or FAILED.
1932 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001933int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934mptscsih_host_reset(struct scsi_cmnd *SCpnt)
1935{
1936 MPT_SCSI_HOST * hd;
1937 int status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938
1939 /* If we can't locate the host to reset, then we failed. */
1940 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001941 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 "Can't locate host! (sc=%p)\n",
1943 SCpnt ) );
1944 return FAILED;
1945 }
1946
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001947 printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 hd->ioc->name, SCpnt);
1949
1950 /* If our attempts to reset the host failed, then return a failed
1951 * status. The host will be taken off line by the SCSI mid-layer.
1952 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
1954 status = FAILED;
1955 } else {
1956 /* Make sure TM pending is cleared and TM state is set to
1957 * NONE.
1958 */
1959 hd->tmPending = 0;
1960 hd->tmState = TM_STATE_NONE;
1961 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001963 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 "Status = %s\n",
1965 (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
1966
1967 return status;
1968}
1969
1970/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1971/**
1972 * mptscsih_tm_pending_wait - wait for pending task management request to
1973 * complete.
1974 * @hd: Pointer to MPT host structure.
1975 *
1976 * Returns {SUCCESS,FAILED}.
1977 */
1978static int
1979mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
1980{
1981 unsigned long flags;
1982 int loop_count = 4 * 10; /* Wait 10 seconds */
1983 int status = FAILED;
1984
1985 do {
1986 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1987 if (hd->tmState == TM_STATE_NONE) {
1988 hd->tmState = TM_STATE_IN_PROGRESS;
1989 hd->tmPending = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001991 status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 break;
1993 }
1994 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1995 msleep(250);
1996 } while (--loop_count);
1997
1998 return status;
1999}
2000
2001/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2002/**
2003 * mptscsih_tm_wait_for_completion - wait for completion of TM task
2004 * @hd: Pointer to MPT host structure.
2005 *
2006 * Returns {SUCCESS,FAILED}.
2007 */
2008static int
2009mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
2010{
2011 unsigned long flags;
2012 int loop_count = 4 * timeout;
2013 int status = FAILED;
2014
2015 do {
2016 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
2017 if(hd->tmPending == 0) {
2018 status = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002019 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 break;
2021 }
2022 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
2023 msleep_interruptible(250);
2024 } while (--loop_count);
2025
2026 return status;
2027}
2028
2029/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2030/**
2031 * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
2032 * @ioc: Pointer to MPT_ADAPTER structure
2033 * @mf: Pointer to SCSI task mgmt request frame
2034 * @mr: Pointer to SCSI task mgmt reply frame
2035 *
2036 * This routine is called from mptbase.c::mpt_interrupt() at the completion
2037 * of any SCSI task management request.
2038 * This routine is registered with the MPT (base) driver at driver
2039 * load/init time via the mpt_register() API call.
2040 *
2041 * Returns 1 indicating alloc'd request frame ptr should be freed.
2042 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002043int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
2045{
2046 SCSITaskMgmtReply_t *pScsiTmReply;
2047 SCSITaskMgmt_t *pScsiTmReq;
2048 MPT_SCSI_HOST *hd;
2049 unsigned long flags;
2050 u16 iocstatus;
2051 u8 tmType;
2052
2053 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
2054 ioc->name, mf, mr));
2055 if (ioc->sh) {
2056 /* Depending on the thread, a timer is activated for
2057 * the TM request. Delete this timer on completion of TM.
2058 * Decrement count of outstanding TM requests.
2059 */
2060 hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
2061 } else {
2062 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
2063 ioc->name));
2064 return 1;
2065 }
2066
2067 if (mr == NULL) {
2068 dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
2069 ioc->name, mf));
2070 return 1;
2071 } else {
2072 pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
2073 pScsiTmReq = (SCSITaskMgmt_t*)mf;
2074
2075 /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
2076 tmType = pScsiTmReq->TaskType;
2077
2078 dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n",
2079 ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
2080 DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply);
2081
2082 iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2083 dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n",
2084 ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo)));
2085 /* Error? (anything non-zero?) */
2086 if (iocstatus) {
2087
2088 /* clear flags and continue.
2089 */
2090 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
2091 hd->abortSCpnt = NULL;
2092
2093 /* If an internal command is present
2094 * or the TM failed - reload the FW.
2095 * FC FW may respond FAILED to an ABORT
2096 */
2097 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
2098 if ((hd->cmdPtr) ||
2099 (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
2100 if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
2101 printk((KERN_WARNING
2102 " Firmware Reload FAILED!!\n"));
2103 }
2104 }
2105 }
2106 } else {
2107 dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name));
2108
2109 hd->abortSCpnt = NULL;
2110
2111 }
2112 }
2113
2114 spin_lock_irqsave(&ioc->FreeQlock, flags);
2115 hd->tmPending = 0;
2116 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2117 hd->tmState = TM_STATE_NONE;
2118
2119 return 1;
2120}
2121
2122/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2123/*
2124 * This is anyones guess quite frankly.
2125 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002126int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
2128 sector_t capacity, int geom[])
2129{
2130 int heads;
2131 int sectors;
2132 sector_t cylinders;
2133 ulong dummy;
2134
2135 heads = 64;
2136 sectors = 32;
2137
2138 dummy = heads * sectors;
2139 cylinders = capacity;
2140 sector_div(cylinders,dummy);
2141
2142 /*
2143 * Handle extended translation size for logical drives
2144 * > 1Gb
2145 */
2146 if ((ulong)capacity >= 0x200000) {
2147 heads = 255;
2148 sectors = 63;
2149 dummy = heads * sectors;
2150 cylinders = capacity;
2151 sector_div(cylinders,dummy);
2152 }
2153
2154 /* return result */
2155 geom[0] = heads;
2156 geom[1] = sectors;
2157 geom[2] = cylinders;
2158
2159 dprintk((KERN_NOTICE
2160 ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n",
2161 sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors));
2162
2163 return 0;
2164}
2165
2166/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2167/*
2168 * OS entry point to allow host driver to alloc memory
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002169 * for each scsi target. Called once per device the bus scan.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 * Return non-zero if allocation fails.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002172int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002173mptscsih_target_alloc(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002175 VirtTarget *vtarget;
2176
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +01002177 vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002178 if (!vtarget)
2179 return -ENOMEM;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002180 starget->hostdata = vtarget;
2181 return 0;
2182}
2183
2184/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2185/*
2186 * OS entry point to allow host driver to alloc memory
2187 * for each scsi device. Called once per device the bus scan.
2188 * Return non-zero if allocation fails.
2189 */
2190int
2191mptscsih_slave_alloc(struct scsi_device *sdev)
2192{
2193 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002195 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 VirtDevice *vdev;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002197 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +01002199 vdev = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 if (!vdev) {
2201 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
2202 hd->ioc->name, sizeof(VirtDevice));
2203 return -ENOMEM;
2204 }
2205
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 vdev->ioc_id = hd->ioc->id;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002207 vdev->target_id = sdev->id;
2208 vdev->bus_id = sdev->channel;
2209 vdev->lun = sdev->lun;
2210 sdev->hostdata = vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002212 starget = scsi_target(sdev);
2213 vtarget = starget->hostdata;
2214 vdev->vtarget = vtarget;
2215
2216 if (vtarget->num_luns == 0) {
2217 hd->Targets[sdev->id] = vtarget;
2218 vtarget->ioc_id = hd->ioc->id;
2219 vtarget->tflags = MPT_TARGET_FLAGS_Q_YES;
2220 vtarget->target_id = sdev->id;
2221 vtarget->bus_id = sdev->channel;
2222 if (hd->ioc->bus_type == SPI) {
2223 if (hd->ioc->raid_data.isRaid & (1 << sdev->id)) {
2224 vtarget->raidVolume = 1;
2225 ddvtprintk((KERN_INFO
2226 "RAID Volume @ id %d\n", sdev->id));
2227 }
2228 } else {
2229 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2230 }
2231 }
2232 vtarget->num_luns++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 return 0;
2234}
2235
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236/*
2237 * OS entry point to allow for host driver to free allocated memory
2238 * Called if no device present or device being unloaded
2239 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002240void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002241mptscsih_target_destroy(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002243 if (starget->hostdata)
2244 kfree(starget->hostdata);
2245 starget->hostdata = NULL;
2246}
2247
2248/*
2249 * OS entry point to allow for host driver to free allocated memory
2250 * Called if no device present or device being unloaded
2251 */
2252void
2253mptscsih_slave_destroy(struct scsi_device *sdev)
2254{
2255 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002257 VirtTarget *vtarget;
2258 VirtDevice *vdevice;
2259 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002261 starget = scsi_target(sdev);
2262 vtarget = starget->hostdata;
2263 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002265 mptscsih_search_running_cmds(hd, vdevice);
2266 vtarget->luns[0] &= ~(1 << vdevice->lun);
2267 vtarget->num_luns--;
2268 if (vtarget->num_luns == 0) {
2269 mptscsih_negotiate_to_asyn_narrow(hd, vtarget);
2270 if (hd->ioc->bus_type == SPI) {
2271 if (mptscsih_is_phys_disk(hd->ioc, vtarget->target_id)) {
2272 hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3;
2273 } else {
2274 hd->ioc->spi_data.dvStatus[vtarget->target_id] =
2275 MPT_SCSICFG_NEGOTIATE;
2276 if (!hd->negoNvram) {
2277 hd->ioc->spi_data.dvStatus[vtarget->target_id] |=
2278 MPT_SCSICFG_DV_NOT_DONE;
2279 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 }
2281 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002282 hd->Targets[sdev->id] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002284 mptscsih_synchronize_cache(hd, vdevice);
2285 kfree(vdevice);
2286 sdev->hostdata = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287}
2288
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002289/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2290/*
2291 * mptscsih_change_queue_depth - This function will set a devices queue depth
2292 * @sdev: per scsi_device pointer
2293 * @qdepth: requested queue depth
2294 *
2295 * Adding support for new 'change_queue_depth' api.
2296*/
2297int
2298mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002300 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
2301 VirtTarget *vtarget;
2302 struct scsi_target *starget;
2303 int max_depth;
2304 int tagged;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002306 starget = scsi_target(sdev);
2307 vtarget = starget->hostdata;
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002308
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002309 if (hd->ioc->bus_type == SPI) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002310 if (vtarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) {
2311 if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 max_depth = 1;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002313 else if (((vtarget->inq_data[0] & 0x1f) == 0x00) &&
2314 (vtarget->minSyncFactor <= MPT_ULTRA160 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2316 else
2317 max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
2318 } else {
2319 /* error case - No Inq. Data */
2320 max_depth = 1;
2321 }
2322 } else
2323 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2324
2325 if (qdepth > max_depth)
2326 qdepth = max_depth;
2327 if (qdepth == 1)
2328 tagged = 0;
2329 else
2330 tagged = MSG_SIMPLE_TAG;
2331
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002332 scsi_adjust_queue_depth(sdev, tagged, qdepth);
2333 return sdev->queue_depth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334}
2335
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336/*
2337 * OS entry point to adjust the queue_depths on a per-device basis.
2338 * Called once per device the bus scan. Use it to force the queue_depth
2339 * member to 1 if a device does not support Q tags.
2340 * Return non-zero if fails.
2341 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002342int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002343mptscsih_slave_configure(struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002345 struct Scsi_Host *sh = sdev->host;
2346 VirtTarget *vtarget;
2347 VirtDevice *vdevice;
2348 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sh->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002350 int indexed_lun, lun_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002352 starget = scsi_target(sdev);
2353 vtarget = starget->hostdata;
2354 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
2356 dsprintk((MYIOC_s_INFO_FMT
2357 "device @ %p, id=%d, LUN=%d, channel=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002358 hd->ioc->name, sdev, sdev->id, sdev->lun, sdev->channel));
2359 if (hd->ioc->bus_type == SPI)
2360 dsprintk((MYIOC_s_INFO_FMT
2361 "sdtr %d wdtr %d ppr %d inq length=%d\n",
2362 hd->ioc->name, sdev->sdtr, sdev->wdtr,
2363 sdev->ppr, sdev->inquiry_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002365 if (sdev->id > sh->max_id) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366 /* error case, should never happen */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002367 scsi_adjust_queue_depth(sdev, 0, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368 goto slave_configure_exit;
2369 }
2370
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002371 vdevice->configured_lun=1;
2372 lun_index = (vdevice->lun >> 5); /* 32 luns per lun_index */
2373 indexed_lun = (vdevice->lun % 32);
2374 vtarget->luns[lun_index] |= (1 << indexed_lun);
2375 mptscsih_initTarget(hd, vtarget, sdev->lun, sdev->inquiry,
2376 sdev->inquiry_len );
2377 mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378
2379 dsprintk((MYIOC_s_INFO_FMT
2380 "Queue depth=%d, tflags=%x\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002381 hd->ioc->name, sdev->queue_depth, vtarget->tflags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002383 if (hd->ioc->bus_type == SPI)
2384 dsprintk((MYIOC_s_INFO_FMT
2385 "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n",
2386 hd->ioc->name, vtarget->negoFlags, vtarget->maxOffset,
2387 vtarget->minSyncFactor));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388
2389slave_configure_exit:
2390
2391 dsprintk((MYIOC_s_INFO_FMT
2392 "tagged %d, simple %d, ordered %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002393 hd->ioc->name,sdev->tagged_supported, sdev->simple_tags,
2394 sdev->ordered_tags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395
2396 return 0;
2397}
2398
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2400/*
2401 * Private routines...
2402 */
2403
2404/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2405/* Utility function to copy sense data from the scsi_cmnd buffer
2406 * to the FC and SCSI target structures.
2407 *
2408 */
2409static void
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002410mptscsih_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 -07002411{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002412 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 SCSIIORequest_t *pReq;
2414 u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415
2416 /* Get target structure
2417 */
2418 pReq = (SCSIIORequest_t *) mf;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002419 vdev = sc->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420
2421 if (sense_count) {
2422 u8 *sense_data;
2423 int req_index;
2424
2425 /* Copy the sense received into the scsi command block. */
2426 req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2427 sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
2428 memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
2429
2430 /* Log SMART data (asc = 0x5D, non-IM case only) if required.
2431 */
2432 if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002433 if ((sense_data[12] == 0x5D) && (vdev->vtarget->raidVolume == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 int idx;
2435 MPT_ADAPTER *ioc = hd->ioc;
2436
2437 idx = ioc->eventContext % ioc->eventLogSize;
2438 ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
2439 ioc->events[idx].eventContext = ioc->eventContext;
2440
2441 ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
2442 (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002443 (sc->device->channel << 8) || sc->device->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444
2445 ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
2446
2447 ioc->eventContext++;
2448 }
2449 }
2450 } else {
2451 dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
2452 hd->ioc->name));
2453 }
2454}
2455
2456static u32
2457SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
2458{
2459 MPT_SCSI_HOST *hd;
2460 int i;
2461
2462 hd = (MPT_SCSI_HOST *) sc->device->host->hostdata;
2463
2464 for (i = 0; i < hd->ioc->req_depth; i++) {
2465 if (hd->ScsiLookup[i] == sc) {
2466 return i;
2467 }
2468 }
2469
2470 return -1;
2471}
2472
2473/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002474int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
2476{
2477 MPT_SCSI_HOST *hd;
2478 unsigned long flags;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002479 int ii;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480
2481 dtmprintk((KERN_WARNING MYNAM
2482 ": IOC %s_reset routed to SCSI host driver!\n",
2483 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
2484 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
2485
2486 /* If a FW reload request arrives after base installed but
2487 * before all scsi hosts have been attached, then an alt_ioc
2488 * may have a NULL sh pointer.
2489 */
2490 if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
2491 return 0;
2492 else
2493 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2494
2495 if (reset_phase == MPT_IOC_SETUP_RESET) {
2496 dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
2497
2498 /* Clean Up:
2499 * 1. Set Hard Reset Pending Flag
2500 * All new commands go to doneQ
2501 */
2502 hd->resetPending = 1;
2503
2504 } else if (reset_phase == MPT_IOC_PRE_RESET) {
2505 dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
2506
2507 /* 2. Flush running commands
2508 * Clean ScsiLookup (and associated memory)
2509 * AND clean mytaskQ
2510 */
2511
2512 /* 2b. Reply to OS all known outstanding I/O commands.
2513 */
2514 mptscsih_flush_running_cmds(hd);
2515
2516 /* 2c. If there was an internal command that
2517 * has not completed, configuration or io request,
2518 * free these resources.
2519 */
2520 if (hd->cmdPtr) {
2521 del_timer(&hd->timer);
2522 mpt_free_msg_frame(ioc, hd->cmdPtr);
2523 }
2524
2525 dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
2526
2527 } else {
2528 dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
2529
2530 /* Once a FW reload begins, all new OS commands are
2531 * redirected to the doneQ w/ a reset status.
2532 * Init all control structures.
2533 */
2534
2535 /* ScsiLookup initialization
2536 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002537 for (ii=0; ii < hd->ioc->req_depth; ii++)
2538 hd->ScsiLookup[ii] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539
2540 /* 2. Chain Buffer initialization
2541 */
2542
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002543 /* 4. Renegotiate to all devices, if SPI
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002545 if (ioc->bus_type == SPI) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n"));
2547 mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM);
2548 }
2549
2550 /* 5. Enable new commands to be posted
2551 */
2552 spin_lock_irqsave(&ioc->FreeQlock, flags);
2553 hd->tmPending = 0;
2554 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2555 hd->resetPending = 0;
2556 hd->tmState = TM_STATE_NONE;
2557
2558 /* 6. If there was an internal command,
2559 * wake this process up.
2560 */
2561 if (hd->cmdPtr) {
2562 /*
2563 * Wake up the original calling thread
2564 */
2565 hd->pLocal = &hd->localReply;
2566 hd->pLocal->completion = MPT_SCANDV_DID_RESET;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002567 hd->scandv_wait_done = 1;
2568 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 hd->cmdPtr = NULL;
2570 }
2571
Michael Reed05e8ec12006-01-13 14:31:54 -06002572 /* 7. SPI: Set flag to force DV and re-read IOC Page 3
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002574 if (ioc->bus_type == SPI) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
2576 ddvtprintk(("Set reload IOC Pg3 Flag\n"));
2577 }
2578
Michael Reed05e8ec12006-01-13 14:31:54 -06002579 /* 7. FC: Rescan for blocked rports which might have returned.
2580 */
2581 else if (ioc->bus_type == FC) {
2582 int work_count;
2583 unsigned long flags;
2584
2585 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
2586 work_count = ++ioc->fc_rescan_work_count;
2587 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
2588 if (work_count == 1)
2589 schedule_work(&ioc->fc_rescan_work);
2590 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591 dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
2592
2593 }
2594
2595 return 1; /* currently means nothing really */
2596}
2597
2598/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002599int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
2601{
2602 MPT_SCSI_HOST *hd;
2603 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
Michael Reed05e8ec12006-01-13 14:31:54 -06002604 int work_count;
2605 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606
2607 devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
2608 ioc->name, event));
2609
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002610 if (ioc->sh == NULL ||
2611 ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL))
2612 return 1;
2613
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 switch (event) {
2615 case MPI_EVENT_UNIT_ATTENTION: /* 03 */
2616 /* FIXME! */
2617 break;
2618 case MPI_EVENT_IOC_BUS_RESET: /* 04 */
2619 case MPI_EVENT_EXT_BUS_RESET: /* 05 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002620 if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1))
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002621 hd->soft_resets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 break;
2623 case MPI_EVENT_LOGOUT: /* 09 */
2624 /* FIXME! */
2625 break;
2626
Michael Reed05e8ec12006-01-13 14:31:54 -06002627 case MPI_EVENT_RESCAN: /* 06 */
2628 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
2629 work_count = ++ioc->fc_rescan_work_count;
2630 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
2631 if (work_count == 1)
2632 schedule_work(&ioc->fc_rescan_work);
2633 break;
2634
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 /*
2636 * CHECKME! Don't think we need to do
2637 * anything for these, but...
2638 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639 case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
2640 case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
2641 /*
2642 * CHECKME! Falling thru...
2643 */
2644 break;
2645
2646 case MPI_EVENT_INTEGRATED_RAID: /* 0B */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002647 {
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07002648#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002649 pMpiEventDataRaid_t pRaidEventData =
2650 (pMpiEventDataRaid_t) pEvReply->Data;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002651 /* Domain Validation Needed */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002652 if (ioc->bus_type == SPI &&
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002653 pRaidEventData->ReasonCode ==
2654 MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED)
2655 mptscsih_set_dvflags_raid(hd, pRaidEventData->PhysDiskNum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656#endif
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002657 break;
2658 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660 case MPI_EVENT_NONE: /* 00 */
2661 case MPI_EVENT_LOG_DATA: /* 01 */
2662 case MPI_EVENT_STATE_CHANGE: /* 02 */
2663 case MPI_EVENT_EVENT_CHANGE: /* 0A */
2664 default:
2665 dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
2666 break;
2667 }
2668
2669 return 1; /* currently means nothing really */
2670}
2671
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2673/*
2674 * mptscsih_initTarget - Target, LUN alloc/free functionality.
2675 * @hd: Pointer to MPT_SCSI_HOST structure
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002676 * @vtarget: per target private data
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 * @lun: SCSI LUN id
2678 * @data: Pointer to data
2679 * @dlen: Number of INQUIRY bytes
2680 *
2681 * NOTE: It's only SAFE to call this routine if data points to
2682 * sane & valid STANDARD INQUIRY data!
2683 *
2684 * Allocate and initialize memory for this target.
2685 * Save inquiry data.
2686 *
2687 */
2688static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002689mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, u8 lun, char *data, int dlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690{
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002691 SpiCfgData *pSpi;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 char data_56;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002693 int inq_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
2695 dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002696 hd->ioc->name, vtarget->bus_id, vtarget->target_id, lun, hd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697
2698 /*
2699 * If the peripheral qualifier filter is enabled then if the target reports a 0x1
2700 * (i.e. The targer is capable of supporting the specified peripheral device type
2701 * on this logical unit; however, the physical device is not currently connected
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002702 * to this logical unit) it will be converted to a 0x3 (i.e. The target is not
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 * capable of supporting a physical device on this logical unit). This is to work
2704 * around a bug in th emid-layer in some distributions in which the mid-layer will
2705 * continue to try to communicate to the LUN and evntually create a dummy LUN.
2706 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002707 if (hd->mpt_pq_filter && dlen && (data[0] & 0xE0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 data[0] |= 0x40;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002709
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 /* Is LUN supported? If so, upper 2 bits will be 0
2711 * in first byte of inquiry data.
2712 */
2713 if (data[0] & 0xe0)
2714 return;
2715
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002716 if (vtarget == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002719 if (data)
2720 vtarget->type = data[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002722 if (hd->ioc->bus_type != SPI)
2723 return;
2724
2725 if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) {
2726 /* Treat all Processors as SAF-TE if
2727 * command line option is set */
2728 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2729 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
2730 }else if ((data[0] == TYPE_PROCESSOR) &&
2731 !(vtarget->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) {
2732 if ( dlen > 49 ) {
2733 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2734 if ( data[44] == 'S' &&
2735 data[45] == 'A' &&
2736 data[46] == 'F' &&
2737 data[47] == '-' &&
2738 data[48] == 'T' &&
2739 data[49] == 'E' ) {
2740 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2741 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 }
2743 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002744 }
2745 if (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) {
2746 inq_len = dlen < 8 ? dlen : 8;
2747 memcpy (vtarget->inq_data, data, inq_len);
2748 /* If have not done DV, set the DV flag.
2749 */
2750 pSpi = &hd->ioc->spi_data;
2751 if ((data[0] == TYPE_TAPE) || (data[0] == TYPE_PROCESSOR)) {
2752 if (pSpi->dvStatus[vtarget->target_id] & MPT_SCSICFG_DV_NOT_DONE)
2753 pSpi->dvStatus[vtarget->target_id] |= MPT_SCSICFG_NEED_DV;
2754 }
2755 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002757 data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */
2758 if (dlen > 56) {
2759 if ( (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2760 /* Update the target capabilities
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002762 data_56 = data[56];
2763 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_56;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002765 }
2766 mptscsih_setTargetNegoParms(hd, vtarget, data_56);
2767 } else {
2768 /* Initial Inquiry may not request enough data bytes to
2769 * obtain byte 57. DV will; if target doesn't return
2770 * at least 57 bytes, data[56] will be zero. */
2771 if (dlen > 56) {
2772 if ( (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2773 /* Update the target capabilities
2774 */
2775 data_56 = data[56];
2776 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_56;
2777 mptscsih_setTargetNegoParms(hd, vtarget, data_56);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 }
2779 }
2780 }
2781}
2782
2783/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2784/*
2785 * Update the target negotiation parameters based on the
2786 * the Inquiry data, adapter capabilities, and NVRAM settings.
2787 *
2788 */
2789static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002790mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *target, char byte56)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791{
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002792 SpiCfgData *pspi_data = &hd->ioc->spi_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 int id = (int) target->target_id;
2794 int nvram;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002795 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 int ii;
2797 u8 width = MPT_NARROW;
2798 u8 factor = MPT_ASYNC;
2799 u8 offset = 0;
2800 u8 version, nfactor;
2801 u8 noQas = 1;
2802
2803 target->negoFlags = pspi_data->noQas;
2804
2805 /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine
2806 * support. If available, default QAS to off and allow enabling.
2807 * If not available, default QAS to on, turn off for non-disks.
2808 */
2809
2810 /* Set flags based on Inquiry data
2811 */
2812 version = target->inq_data[2] & 0x07;
2813 if (version < 2) {
2814 width = 0;
2815 factor = MPT_ULTRA2;
2816 offset = pspi_data->maxSyncOffset;
2817 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2818 } else {
2819 if (target->inq_data[7] & 0x20) {
2820 width = 1;
2821 }
2822
2823 if (target->inq_data[7] & 0x10) {
2824 factor = pspi_data->minSyncFactor;
2825 if (target->tflags & MPT_TARGET_FLAGS_VALID_56) {
2826 /* bits 2 & 3 show Clocking support */
2827 if ((byte56 & 0x0C) == 0)
2828 factor = MPT_ULTRA2;
2829 else {
2830 if ((byte56 & 0x03) == 0)
2831 factor = MPT_ULTRA160;
2832 else {
2833 factor = MPT_ULTRA320;
2834 if (byte56 & 0x02)
2835 {
2836 ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id));
2837 noQas = 0;
2838 }
2839 if (target->inq_data[0] == TYPE_TAPE) {
2840 if (byte56 & 0x01)
2841 target->negoFlags |= MPT_TAPE_NEGO_IDP;
2842 }
2843 }
2844 }
2845 } else {
2846 ddvtprintk((KERN_INFO "Enabling QAS on id=%d due to ~TARGET_FLAGS_VALID_56!\n", id));
2847 noQas = 0;
2848 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002849
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 offset = pspi_data->maxSyncOffset;
2851
2852 /* If RAID, never disable QAS
2853 * else if non RAID, do not disable
2854 * QAS if bit 1 is set
2855 * bit 1 QAS support, non-raid only
2856 * bit 0 IU support
2857 */
2858 if (target->raidVolume == 1) {
2859 noQas = 0;
2860 }
2861 } else {
2862 factor = MPT_ASYNC;
2863 offset = 0;
2864 }
2865 }
2866
2867 if ( (target->inq_data[7] & 0x02) == 0) {
2868 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2869 }
2870
2871 /* Update tflags based on NVRAM settings. (SCSI only)
2872 */
2873 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
2874 nvram = pspi_data->nvram[id];
2875 nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
2876
2877 if (width)
2878 width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
2879
2880 if (offset > 0) {
2881 /* Ensure factor is set to the
2882 * maximum of: adapter, nvram, inquiry
2883 */
2884 if (nfactor) {
2885 if (nfactor < pspi_data->minSyncFactor )
2886 nfactor = pspi_data->minSyncFactor;
2887
2888 factor = max(factor, nfactor);
2889 if (factor == MPT_ASYNC)
2890 offset = 0;
2891 } else {
2892 offset = 0;
2893 factor = MPT_ASYNC;
2894 }
2895 } else {
2896 factor = MPT_ASYNC;
2897 }
2898 }
2899
2900 /* Make sure data is consistent
2901 */
2902 if ((!width) && (factor < MPT_ULTRA2)) {
2903 factor = MPT_ULTRA2;
2904 }
2905
2906 /* Save the data to the target structure.
2907 */
2908 target->minSyncFactor = factor;
2909 target->maxOffset = offset;
2910 target->maxWidth = width;
2911
2912 target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
2913
2914 /* Disable unused features.
2915 */
2916 if (!width)
2917 target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
2918
2919 if (!offset)
2920 target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
2921
2922 if ( factor > MPT_ULTRA320 )
2923 noQas = 0;
2924
2925 /* GEM, processor WORKAROUND
2926 */
2927 if ((target->inq_data[0] == TYPE_PROCESSOR) || (target->inq_data[0] > 0x08)) {
2928 target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC);
2929 pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO;
2930 } else {
2931 if (noQas && (pspi_data->noQas == 0)) {
2932 pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS;
2933 target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2934
2935 /* Disable QAS in a mixed configuration case
2936 */
2937
2938 ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id));
2939 for (ii = 0; ii < id; ii++) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002940 if ( (vtarget = hd->Targets[ii]) ) {
2941 vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2942 mptscsih_writeSDP1(hd, 0, ii, vtarget->negoFlags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002943 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944 }
2945 }
2946 }
2947
2948 /* Write SDP1 on this I/O to this target */
2949 if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) {
2950 ddvtprintk((KERN_INFO "MPT_SCSICFG_NEGOTIATE on id=%d!\n", id));
2951 mptscsih_writeSDP1(hd, 0, id, hd->negoNvram);
2952 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE;
2953 } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) {
2954 ddvtprintk((KERN_INFO "MPT_SCSICFG_BLK_NEGO on id=%d!\n", id));
2955 mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO);
2956 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO;
2957 }
2958}
2959
2960/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961/*
2962 * If no Target, bus reset on 1st I/O. Set the flag to
2963 * prevent any future negotiations to this device.
2964 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002965static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002966mptscsih_no_negotiate(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002968 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002970 if ((vdev = sc->device->hostdata) != NULL)
2971 hd->ioc->spi_data.dvStatus[vdev->target_id] |= MPT_SCSICFG_BLK_NEGO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972 return;
2973}
2974
2975/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2976/*
2977 * SCSI Config Page functionality ...
2978 */
2979/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2980/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags
2981 * based on width, factor and offset parameters.
2982 * @width: bus width
2983 * @factor: sync factor
2984 * @offset: sync offset
2985 * @requestedPtr: pointer to requested values (updated)
2986 * @configurationPtr: pointer to configuration values (updated)
2987 * @flags: flags to block WDTR or SDTR negotiation
2988 *
2989 * Return: None.
2990 *
2991 * Remark: Called by writeSDP1 and _dv_params
2992 */
2993static void
2994mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags)
2995{
2996 u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE;
2997 u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC;
2998
2999 *configurationPtr = 0;
3000 *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0;
3001 *requestedPtr |= (offset << 16) | (factor << 8);
3002
3003 if (width && offset && !nowide && !nosync) {
3004 if (factor < MPT_ULTRA160) {
3005 *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT);
3006 if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0)
3007 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS;
3008 if (flags & MPT_TAPE_NEGO_IDP)
3009 *requestedPtr |= 0x08000000;
3010 } else if (factor < MPT_ULTRA2) {
3011 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT;
3012 }
3013 }
3014
3015 if (nowide)
3016 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED;
3017
3018 if (nosync)
3019 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED;
3020
3021 return;
3022}
3023
3024/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3025/* mptscsih_writeSDP1 - write SCSI Device Page 1
3026 * @hd: Pointer to a SCSI Host Strucutre
3027 * @portnum: IOC port number
3028 * @target_id: writeSDP1 for single ID
3029 * @flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO
3030 *
3031 * Return: -EFAULT if read of config page header fails
3032 * or 0 if success.
3033 *
3034 * Remark: If a target has been found, the settings from the
3035 * target structure are used, else the device is set
3036 * to async/narrow.
3037 *
3038 * Remark: Called during init and after a FW reload.
3039 * Remark: We do not wait for a return, write pages sequentially.
3040 */
3041static int
3042mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags)
3043{
3044 MPT_ADAPTER *ioc = hd->ioc;
3045 Config_t *pReq;
3046 SCSIDevicePage1_t *pData;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003047 VirtTarget *vtarget=NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 MPT_FRAME_HDR *mf;
3049 dma_addr_t dataDma;
3050 u16 req_idx;
3051 u32 frameOffset;
3052 u32 requested, configuration, flagsLength;
3053 int ii, nvram;
3054 int id = 0, maxid = 0;
3055 u8 width;
3056 u8 factor;
3057 u8 offset;
3058 u8 bus = 0;
3059 u8 negoFlags;
3060 u8 maxwidth, maxoffset, maxfactor;
3061
3062 if (ioc->spi_data.sdp1length == 0)
3063 return 0;
3064
3065 if (flags & MPT_SCSICFG_ALL_IDS) {
3066 id = 0;
3067 maxid = ioc->sh->max_id - 1;
3068 } else if (ioc->sh) {
3069 id = target_id;
3070 maxid = min_t(int, id, ioc->sh->max_id - 1);
3071 }
3072
3073 for (; id <= maxid; id++) {
3074
3075 if (id == ioc->pfacts[portnum].PortSCSIID)
3076 continue;
3077
3078 /* Use NVRAM to get adapter and target maximums
3079 * Data over-riden by target structure information, if present
3080 */
3081 maxwidth = ioc->spi_data.maxBusWidth;
3082 maxoffset = ioc->spi_data.maxSyncOffset;
3083 maxfactor = ioc->spi_data.minSyncFactor;
3084 if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3085 nvram = ioc->spi_data.nvram[id];
3086
3087 if (maxwidth)
3088 maxwidth = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
3089
3090 if (maxoffset > 0) {
3091 maxfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
3092 if (maxfactor == 0) {
3093 /* Key for async */
3094 maxfactor = MPT_ASYNC;
3095 maxoffset = 0;
3096 } else if (maxfactor < ioc->spi_data.minSyncFactor) {
3097 maxfactor = ioc->spi_data.minSyncFactor;
3098 }
3099 } else
3100 maxfactor = MPT_ASYNC;
3101 }
3102
3103 /* Set the negotiation flags.
3104 */
3105 negoFlags = ioc->spi_data.noQas;
3106 if (!maxwidth)
3107 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
3108
3109 if (!maxoffset)
3110 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
3111
3112 if (flags & MPT_SCSICFG_USE_NVRAM) {
3113 width = maxwidth;
3114 factor = maxfactor;
3115 offset = maxoffset;
3116 } else {
3117 width = 0;
3118 factor = MPT_ASYNC;
3119 offset = 0;
3120 //negoFlags = 0;
3121 //negoFlags = MPT_TARGET_NO_NEGO_SYNC;
3122 }
3123
3124 /* If id is not a raid volume, get the updated
3125 * transmission settings from the target structure.
3126 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003127 if (hd->Targets && (vtarget = hd->Targets[id]) && !vtarget->raidVolume) {
3128 width = vtarget->maxWidth;
3129 factor = vtarget->minSyncFactor;
3130 offset = vtarget->maxOffset;
3131 negoFlags = vtarget->negoFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003133
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3135 /* Force to async and narrow if DV has not been executed
3136 * for this ID
3137 */
3138 if ((hd->ioc->spi_data.dvStatus[id] & MPT_SCSICFG_DV_NOT_DONE) != 0) {
3139 width = 0;
3140 factor = MPT_ASYNC;
3141 offset = 0;
3142 }
3143#endif
3144
3145 if (flags & MPT_SCSICFG_BLK_NEGO)
Moore, Eric Dean466544d2005-09-14 18:09:10 -06003146 negoFlags |= MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147
3148 mptscsih_setDevicePage1Flags(width, factor, offset,
3149 &requested, &configuration, negoFlags);
3150 dnegoprintk(("writeSDP1: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
3151 target_id, width, factor, offset, negoFlags, requested, configuration));
3152
3153 /* Get a MF for this command.
3154 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003155 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003156 dfailprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n",
3157 ioc->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158 return -EAGAIN;
3159 }
3160
3161 ddvprintk((MYIOC_s_INFO_FMT "WriteSDP1 (mf=%p, id=%d, req=0x%x, cfg=0x%x)\n",
3162 hd->ioc->name, mf, id, requested, configuration));
3163
3164
3165 /* Set the request and the data pointers.
3166 * Request takes: 36 bytes (32 bit SGE)
3167 * SCSI Device Page 1 requires 16 bytes
3168 * 40 + 16 <= size of SCSI IO Request = 56 bytes
3169 * and MF size >= 64 bytes.
3170 * Place data at end of MF.
3171 */
3172 pReq = (Config_t *)mf;
3173
3174 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3175 frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t);
3176
3177 pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset);
3178 dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset;
3179
3180 /* Complete the request frame (same for all requests).
3181 */
3182 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3183 pReq->Reserved = 0;
3184 pReq->ChainOffset = 0;
3185 pReq->Function = MPI_FUNCTION_CONFIG;
3186 pReq->ExtPageLength = 0;
3187 pReq->ExtPageType = 0;
3188 pReq->MsgFlags = 0;
3189 for (ii=0; ii < 8; ii++) {
3190 pReq->Reserved2[ii] = 0;
3191 }
3192 pReq->Header.PageVersion = ioc->spi_data.sdp1version;
3193 pReq->Header.PageLength = ioc->spi_data.sdp1length;
3194 pReq->Header.PageNumber = 1;
3195 pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
3196 pReq->PageAddress = cpu_to_le32(id | (bus << 8 ));
3197
3198 /* Add a SGE to the config request.
3199 */
3200 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4;
3201
3202 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3203
3204 /* Set up the common data portion
3205 */
3206 pData->Header.PageVersion = pReq->Header.PageVersion;
3207 pData->Header.PageLength = pReq->Header.PageLength;
3208 pData->Header.PageNumber = pReq->Header.PageNumber;
3209 pData->Header.PageType = pReq->Header.PageType;
3210 pData->RequestedParameters = cpu_to_le32(requested);
3211 pData->Reserved = 0;
3212 pData->Configuration = cpu_to_le32(configuration);
3213
3214 dprintk((MYIOC_s_INFO_FMT
3215 "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n",
3216 ioc->name, id, (id | (bus<<8)),
3217 requested, configuration));
3218
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003219 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220 }
3221
3222 return 0;
3223}
3224
3225/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3226/* mptscsih_writeIOCPage4 - write IOC Page 4
3227 * @hd: Pointer to a SCSI Host Structure
3228 * @target_id: write IOC Page4 for this ID & Bus
3229 *
3230 * Return: -EAGAIN if unable to obtain a Message Frame
3231 * or 0 if success.
3232 *
3233 * Remark: We do not wait for a return, write pages sequentially.
3234 */
3235static int
3236mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus)
3237{
3238 MPT_ADAPTER *ioc = hd->ioc;
3239 Config_t *pReq;
3240 IOCPage4_t *IOCPage4Ptr;
3241 MPT_FRAME_HDR *mf;
3242 dma_addr_t dataDma;
3243 u16 req_idx;
3244 u32 frameOffset;
3245 u32 flagsLength;
3246 int ii;
3247
3248 /* Get a MF for this command.
3249 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003250 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003251 dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252 ioc->name));
3253 return -EAGAIN;
3254 }
3255
3256 /* Set the request and the data pointers.
3257 * Place data at end of MF.
3258 */
3259 pReq = (Config_t *)mf;
3260
3261 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3262 frameOffset = ioc->req_sz - sizeof(IOCPage4_t);
3263
3264 /* Complete the request frame (same for all requests).
3265 */
3266 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3267 pReq->Reserved = 0;
3268 pReq->ChainOffset = 0;
3269 pReq->Function = MPI_FUNCTION_CONFIG;
3270 pReq->ExtPageLength = 0;
3271 pReq->ExtPageType = 0;
3272 pReq->MsgFlags = 0;
3273 for (ii=0; ii < 8; ii++) {
3274 pReq->Reserved2[ii] = 0;
3275 }
3276
3277 IOCPage4Ptr = ioc->spi_data.pIocPg4;
3278 dataDma = ioc->spi_data.IocPg4_dma;
3279 ii = IOCPage4Ptr->ActiveSEP++;
3280 IOCPage4Ptr->SEP[ii].SEPTargetID = target_id;
3281 IOCPage4Ptr->SEP[ii].SEPBus = bus;
3282 pReq->Header = IOCPage4Ptr->Header;
3283 pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 ));
3284
3285 /* Add a SGE to the config request.
3286 */
3287 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
3288 (IOCPage4Ptr->Header.PageLength + ii) * 4;
3289
3290 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3291
3292 dinitprintk((MYIOC_s_INFO_FMT
3293 "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
3294 ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus));
3295
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003296 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003297
3298 return 0;
3299}
3300
3301/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3302/*
3303 * Bus Scan and Domain Validation functionality ...
3304 */
3305
3306/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3307/*
3308 * mptscsih_scandv_complete - Scan and DV callback routine registered
3309 * to Fustion MPT (base) driver.
3310 *
3311 * @ioc: Pointer to MPT_ADAPTER structure
3312 * @mf: Pointer to original MPT request frame
3313 * @mr: Pointer to MPT reply frame (NULL if TurboReply)
3314 *
3315 * This routine is called from mpt.c::mpt_interrupt() at the completion
3316 * of any SCSI IO request.
3317 * This routine is registered with the Fusion MPT (base) driver at driver
3318 * load/init time via the mpt_register() API call.
3319 *
3320 * Returns 1 indicating alloc'd request frame ptr should be freed.
3321 *
3322 * Remark: Sets a completion code and (possibly) saves sense data
3323 * in the IOC member localReply structure.
3324 * Used ONLY for DV and other internal commands.
3325 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003326int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
3328{
3329 MPT_SCSI_HOST *hd;
3330 SCSIIORequest_t *pReq;
3331 int completionCode;
3332 u16 req_idx;
3333
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003334 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
3335
Linus Torvalds1da177e2005-04-16 15:20:36 -07003336 if ((mf == NULL) ||
3337 (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
3338 printk(MYIOC_s_ERR_FMT
3339 "ScanDvComplete, %s req frame ptr! (=%p)\n",
3340 ioc->name, mf?"BAD":"NULL", (void *) mf);
3341 goto wakeup;
3342 }
3343
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344 del_timer(&hd->timer);
3345 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3346 hd->ScsiLookup[req_idx] = NULL;
3347 pReq = (SCSIIORequest_t *) mf;
3348
3349 if (mf != hd->cmdPtr) {
3350 printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
3351 hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
3352 }
3353 hd->cmdPtr = NULL;
3354
3355 ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
3356 hd->ioc->name, mf, mr, req_idx));
3357
3358 hd->pLocal = &hd->localReply;
3359 hd->pLocal->scsiStatus = 0;
3360
3361 /* If target struct exists, clear sense valid flag.
3362 */
3363 if (mr == NULL) {
3364 completionCode = MPT_SCANDV_GOOD;
3365 } else {
3366 SCSIIOReply_t *pReply;
3367 u16 status;
3368 u8 scsi_status;
3369
3370 pReply = (SCSIIOReply_t *) mr;
3371
3372 status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
3373 scsi_status = pReply->SCSIStatus;
3374
3375 ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
3376 status, pReply->SCSIState, scsi_status,
3377 le32_to_cpu(pReply->IOCLogInfo)));
3378
3379 switch(status) {
3380
3381 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
3382 completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
3383 break;
3384
3385 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
3386 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
3387 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
3388 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
3389 completionCode = MPT_SCANDV_DID_RESET;
3390 break;
3391
3392 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
3393 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
3394 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
3395 if (pReply->Function == MPI_FUNCTION_CONFIG) {
3396 ConfigReply_t *pr = (ConfigReply_t *)mr;
3397 completionCode = MPT_SCANDV_GOOD;
3398 hd->pLocal->header.PageVersion = pr->Header.PageVersion;
3399 hd->pLocal->header.PageLength = pr->Header.PageLength;
3400 hd->pLocal->header.PageNumber = pr->Header.PageNumber;
3401 hd->pLocal->header.PageType = pr->Header.PageType;
3402
3403 } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
3404 /* If the RAID Volume request is successful,
3405 * return GOOD, else indicate that
3406 * some type of error occurred.
3407 */
3408 MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
Christoph Hellwig637fa992005-08-18 16:25:44 +02003409 if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003410 completionCode = MPT_SCANDV_GOOD;
3411 else
3412 completionCode = MPT_SCANDV_SOME_ERROR;
3413
3414 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
3415 u8 *sense_data;
3416 int sz;
3417
3418 /* save sense data in global structure
3419 */
3420 completionCode = MPT_SCANDV_SENSE;
3421 hd->pLocal->scsiStatus = scsi_status;
3422 sense_data = ((u8 *)hd->ioc->sense_buf_pool +
3423 (req_idx * MPT_SENSE_BUFFER_ALLOC));
3424
3425 sz = min_t(int, pReq->SenseBufferLength,
3426 SCSI_STD_SENSE_BYTES);
3427 memcpy(hd->pLocal->sense, sense_data, sz);
3428
3429 ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
3430 sense_data));
3431 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
3432 if (pReq->CDB[0] == INQUIRY)
3433 completionCode = MPT_SCANDV_ISSUE_SENSE;
3434 else
3435 completionCode = MPT_SCANDV_DID_RESET;
3436 }
3437 else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
3438 completionCode = MPT_SCANDV_DID_RESET;
3439 else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3440 completionCode = MPT_SCANDV_DID_RESET;
3441 else {
3442 completionCode = MPT_SCANDV_GOOD;
3443 hd->pLocal->scsiStatus = scsi_status;
3444 }
3445 break;
3446
3447 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
3448 if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3449 completionCode = MPT_SCANDV_DID_RESET;
3450 else
3451 completionCode = MPT_SCANDV_SOME_ERROR;
3452 break;
3453
3454 default:
3455 completionCode = MPT_SCANDV_SOME_ERROR;
3456 break;
3457
3458 } /* switch(status) */
3459
3460 ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n",
3461 completionCode));
3462 } /* end of address reply case */
3463
3464 hd->pLocal->completion = completionCode;
3465
3466 /* MF and RF are freed in mpt_interrupt
3467 */
3468wakeup:
3469 /* Free Chain buffers (will never chain) in scan or dv */
3470 //mptscsih_freeChainBuffers(ioc, req_idx);
3471
3472 /*
3473 * Wake up the original calling thread
3474 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003475 hd->scandv_wait_done = 1;
3476 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477
3478 return 1;
3479}
3480
3481/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3482/* mptscsih_timer_expired - Call back for timer process.
3483 * Used only for dv functionality.
3484 * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
3485 *
3486 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003487void
3488mptscsih_timer_expired(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489{
3490 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
3491
3492 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
3493
3494 if (hd->cmdPtr) {
3495 MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
3496
3497 if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
3498 /* Desire to issue a task management request here.
3499 * TM requests MUST be single threaded.
3500 * If old eh code and no TM current, issue request.
3501 * If new eh code, do nothing. Wait for OS cmd timeout
3502 * for bus reset.
3503 */
3504 ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
3505 } else {
3506 /* Perform a FW reload */
3507 if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
3508 printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
3509 }
3510 }
3511 } else {
3512 /* This should NEVER happen */
3513 printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
3514 }
3515
3516 /* No more processing.
3517 * TM call will generate an interrupt for SCSI TM Management.
3518 * The FW will reply to all outstanding commands, callback will finish cleanup.
3519 * Hard reset clean-up will free all resources.
3520 */
3521 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
3522
3523 return;
3524}
3525
3526#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3527/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3528/* mptscsih_do_raid - Format and Issue a RAID volume request message.
3529 * @hd: Pointer to scsi host structure
3530 * @action: What do be done.
3531 * @id: Logical target id.
3532 * @bus: Target locations bus.
3533 *
3534 * Returns: < 0 on a fatal error
3535 * 0 on success
3536 *
3537 * Remark: Wait to return until reply processed by the ISR.
3538 */
3539static int
3540mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io)
3541{
3542 MpiRaidActionRequest_t *pReq;
3543 MPT_FRAME_HDR *mf;
3544 int in_isr;
3545
3546 in_isr = in_interrupt();
3547 if (in_isr) {
3548 dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n",
3549 hd->ioc->name));
3550 return -EPERM;
3551 }
3552
3553 /* Get and Populate a free Frame
3554 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003555 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003556 ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
3557 hd->ioc->name));
3558 return -EAGAIN;
3559 }
3560 pReq = (MpiRaidActionRequest_t *)mf;
3561 pReq->Action = action;
3562 pReq->Reserved1 = 0;
3563 pReq->ChainOffset = 0;
3564 pReq->Function = MPI_FUNCTION_RAID_ACTION;
3565 pReq->VolumeID = io->id;
3566 pReq->VolumeBus = io->bus;
3567 pReq->PhysDiskNum = io->physDiskNum;
3568 pReq->MsgFlags = 0;
3569 pReq->Reserved2 = 0;
3570 pReq->ActionDataWord = 0; /* Reserved for this action */
3571 //pReq->ActionDataSGE = 0;
3572
3573 mpt_add_sge((char *)&pReq->ActionDataSGE,
3574 MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
3575
3576 ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n",
3577 hd->ioc->name, action, io->id));
3578
3579 hd->pLocal = NULL;
3580 hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003581 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003582
3583 /* Save cmd pointer, for resource free if timeout or
3584 * FW reload occurs
3585 */
3586 hd->cmdPtr = mf;
3587
3588 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003589 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3590 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003591
3592 if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD))
3593 return -1;
3594
3595 return 0;
3596}
3597#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
3598
3599/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3600/**
3601 * mptscsih_do_cmd - Do internal command.
3602 * @hd: MPT_SCSI_HOST pointer
3603 * @io: INTERNAL_CMD pointer.
3604 *
3605 * Issue the specified internally generated command and do command
3606 * specific cleanup. For bus scan / DV only.
3607 * NOTES: If command is Inquiry and status is good,
3608 * initialize a target structure, save the data
3609 *
3610 * Remark: Single threaded access only.
3611 *
3612 * Return:
3613 * < 0 if an illegal command or no resources
3614 *
3615 * 0 if good
3616 *
3617 * > 0 if command complete but some type of completion error.
3618 */
3619static int
3620mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
3621{
3622 MPT_FRAME_HDR *mf;
3623 SCSIIORequest_t *pScsiReq;
3624 SCSIIORequest_t ReqCopy;
3625 int my_idx, ii, dir;
3626 int rc, cmdTimeout;
3627 int in_isr;
3628 char cmdLen;
3629 char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3630 char cmd = io->cmd;
3631
3632 in_isr = in_interrupt();
3633 if (in_isr) {
3634 dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
3635 hd->ioc->name));
3636 return -EPERM;
3637 }
3638
3639
3640 /* Set command specific information
3641 */
3642 switch (cmd) {
3643 case INQUIRY:
3644 cmdLen = 6;
3645 dir = MPI_SCSIIO_CONTROL_READ;
3646 CDB[0] = cmd;
3647 CDB[4] = io->size;
3648 cmdTimeout = 10;
3649 break;
3650
3651 case TEST_UNIT_READY:
3652 cmdLen = 6;
3653 dir = MPI_SCSIIO_CONTROL_READ;
3654 cmdTimeout = 10;
3655 break;
3656
3657 case START_STOP:
3658 cmdLen = 6;
3659 dir = MPI_SCSIIO_CONTROL_READ;
3660 CDB[0] = cmd;
3661 CDB[4] = 1; /*Spin up the disk */
3662 cmdTimeout = 15;
3663 break;
3664
3665 case REQUEST_SENSE:
3666 cmdLen = 6;
3667 CDB[0] = cmd;
3668 CDB[4] = io->size;
3669 dir = MPI_SCSIIO_CONTROL_READ;
3670 cmdTimeout = 10;
3671 break;
3672
3673 case READ_BUFFER:
3674 cmdLen = 10;
3675 dir = MPI_SCSIIO_CONTROL_READ;
3676 CDB[0] = cmd;
3677 if (io->flags & MPT_ICFLAG_ECHO) {
3678 CDB[1] = 0x0A;
3679 } else {
3680 CDB[1] = 0x02;
3681 }
3682
3683 if (io->flags & MPT_ICFLAG_BUF_CAP) {
3684 CDB[1] |= 0x01;
3685 }
3686 CDB[6] = (io->size >> 16) & 0xFF;
3687 CDB[7] = (io->size >> 8) & 0xFF;
3688 CDB[8] = io->size & 0xFF;
3689 cmdTimeout = 10;
3690 break;
3691
3692 case WRITE_BUFFER:
3693 cmdLen = 10;
3694 dir = MPI_SCSIIO_CONTROL_WRITE;
3695 CDB[0] = cmd;
3696 if (io->flags & MPT_ICFLAG_ECHO) {
3697 CDB[1] = 0x0A;
3698 } else {
3699 CDB[1] = 0x02;
3700 }
3701 CDB[6] = (io->size >> 16) & 0xFF;
3702 CDB[7] = (io->size >> 8) & 0xFF;
3703 CDB[8] = io->size & 0xFF;
3704 cmdTimeout = 10;
3705 break;
3706
3707 case RESERVE:
3708 cmdLen = 6;
3709 dir = MPI_SCSIIO_CONTROL_READ;
3710 CDB[0] = cmd;
3711 cmdTimeout = 10;
3712 break;
3713
3714 case RELEASE:
3715 cmdLen = 6;
3716 dir = MPI_SCSIIO_CONTROL_READ;
3717 CDB[0] = cmd;
3718 cmdTimeout = 10;
3719 break;
3720
3721 case SYNCHRONIZE_CACHE:
3722 cmdLen = 10;
3723 dir = MPI_SCSIIO_CONTROL_READ;
3724 CDB[0] = cmd;
3725// CDB[1] = 0x02; /* set immediate bit */
3726 cmdTimeout = 10;
3727 break;
3728
3729 default:
3730 /* Error Case */
3731 return -EFAULT;
3732 }
3733
3734 /* Get and Populate a free Frame
3735 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003736 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
3738 hd->ioc->name));
3739 return -EBUSY;
3740 }
3741
3742 pScsiReq = (SCSIIORequest_t *) mf;
3743
3744 /* Get the request index */
3745 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3746 ADD_INDEX_LOG(my_idx); /* for debug */
3747
3748 if (io->flags & MPT_ICFLAG_PHYS_DISK) {
3749 pScsiReq->TargetID = io->physDiskNum;
3750 pScsiReq->Bus = 0;
3751 pScsiReq->ChainOffset = 0;
3752 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
3753 } else {
3754 pScsiReq->TargetID = io->id;
3755 pScsiReq->Bus = io->bus;
3756 pScsiReq->ChainOffset = 0;
3757 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
3758 }
3759
3760 pScsiReq->CDBLength = cmdLen;
3761 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
3762
3763 pScsiReq->Reserved = 0;
3764
3765 pScsiReq->MsgFlags = mpt_msg_flags();
3766 /* MsgContext set in mpt_get_msg_fram call */
3767
3768 for (ii=0; ii < 8; ii++)
3769 pScsiReq->LUN[ii] = 0;
3770 pScsiReq->LUN[1] = io->lun;
3771
3772 if (io->flags & MPT_ICFLAG_TAGGED_CMD)
3773 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
3774 else
3775 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3776
3777 if (cmd == REQUEST_SENSE) {
3778 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3779 ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n",
3780 hd->ioc->name, cmd));
3781 }
3782
3783 for (ii=0; ii < 16; ii++)
3784 pScsiReq->CDB[ii] = CDB[ii];
3785
3786 pScsiReq->DataLength = cpu_to_le32(io->size);
3787 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
3788 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
3789
3790 ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
3791 hd->ioc->name, cmd, io->bus, io->id, io->lun));
3792
3793 if (dir == MPI_SCSIIO_CONTROL_READ) {
3794 mpt_add_sge((char *) &pScsiReq->SGL,
3795 MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
3796 io->data_dma);
3797 } else {
3798 mpt_add_sge((char *) &pScsiReq->SGL,
3799 MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
3800 io->data_dma);
3801 }
3802
3803 /* The ISR will free the request frame, but we need
3804 * the information to initialize the target. Duplicate.
3805 */
3806 memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
3807
3808 /* Issue this command after:
3809 * finish init
3810 * add timer
3811 * Wait until the reply has been received
3812 * ScsiScanDvCtx callback function will
3813 * set hd->pLocal;
3814 * set scandv_wait_done and call wake_up
3815 */
3816 hd->pLocal = NULL;
3817 hd->timer.expires = jiffies + HZ*cmdTimeout;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003818 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819
3820 /* Save cmd pointer, for resource free if timeout or
3821 * FW reload occurs
3822 */
3823 hd->cmdPtr = mf;
3824
3825 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003826 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3827 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003828
3829 if (hd->pLocal) {
3830 rc = hd->pLocal->completion;
3831 hd->pLocal->skip = 0;
3832
3833 /* Always set fatal error codes in some cases.
3834 */
3835 if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
3836 rc = -ENXIO;
3837 else if (rc == MPT_SCANDV_SOME_ERROR)
3838 rc = -rc;
3839 } else {
3840 rc = -EFAULT;
3841 /* This should never happen. */
3842 ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
3843 hd->ioc->name));
3844 }
3845
3846 return rc;
3847}
3848
3849/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3850/**
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003851 * mptscsih_negotiate_to_asyn_narrow - Restore devices to default state
3852 * @hd: Pointer to a SCSI HOST structure
3853 * @vtarget: per device private data
Linus Torvalds1da177e2005-04-16 15:20:36 -07003854 *
3855 * Uses the ISR, but with special processing.
3856 * MUST be single-threaded.
3857 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07003858 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003859static void
3860mptscsih_negotiate_to_asyn_narrow(MPT_SCSI_HOST *hd, VirtTarget *vtarget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003861{
3862 MPT_ADAPTER *ioc= hd->ioc;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003863 SCSIDevicePage1_t *pcfg1Data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003864 CONFIGPARMS cfg;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003865 dma_addr_t cfg1_dma_addr;
3866 ConfigPageHeader_t header;
3867 int id;
3868 int requested, configuration, data,i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003869 u8 flags, factor;
3870
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003871 if (ioc->bus_type != SPI)
3872 return;
3873
3874 if (!ioc->spi_data.sdp1length)
3875 return;
3876
3877 pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev,
3878 ioc->spi_data.sdp1length * 4, &cfg1_dma_addr);
3879
3880 if (pcfg1Data == NULL)
3881 return;
3882
3883 header.PageVersion = ioc->spi_data.sdp1version;
3884 header.PageLength = ioc->spi_data.sdp1length;
3885 header.PageNumber = 1;
3886 header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
3887 cfg.cfghdr.hdr = &header;
3888 cfg.physAddr = cfg1_dma_addr;
3889 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3890 cfg.dir = 1;
3891 cfg.timeout = 0;
3892
3893 if (vtarget->raidVolume && ioc->raid_data.pIocPg3) {
3894 for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
3895 id = ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID;
3896 flags = hd->ioc->spi_data.noQas;
3897 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3898 data = hd->ioc->spi_data.nvram[id];
3899 if (data & MPT_NVRAM_WIDE_DISABLE)
3900 flags |= MPT_TARGET_NO_NEGO_WIDE;
3901 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
3902 if ((factor == 0) || (factor == MPT_ASYNC))
3903 flags |= MPT_TARGET_NO_NEGO_SYNC;
3904 }
3905 mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
3906 &configuration, flags);
3907 dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC "
3908 "offset=0 negoFlags=%x request=%x config=%x\n",
3909 id, flags, requested, configuration));
3910 pcfg1Data->RequestedParameters = cpu_to_le32(requested);
3911 pcfg1Data->Reserved = 0;
3912 pcfg1Data->Configuration = cpu_to_le32(configuration);
3913 cfg.pageAddr = (vtarget->bus_id<<8) | id;
3914 mpt_config(hd->ioc, &cfg);
3915 }
3916 } else {
3917 flags = vtarget->negoFlags;
3918 mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
3919 &configuration, flags);
3920 dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC "
3921 "offset=0 negoFlags=%x request=%x config=%x\n",
3922 vtarget->target_id, flags, requested, configuration));
3923 pcfg1Data->RequestedParameters = cpu_to_le32(requested);
3924 pcfg1Data->Reserved = 0;
3925 pcfg1Data->Configuration = cpu_to_le32(configuration);
3926 cfg.pageAddr = (vtarget->bus_id<<8) | vtarget->target_id;
3927 mpt_config(hd->ioc, &cfg);
3928 }
3929
3930 if (pcfg1Data)
3931 pci_free_consistent(ioc->pcidev, header.PageLength * 4, pcfg1Data, cfg1_dma_addr);
3932}
3933
3934/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3935/**
3936 * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
3937 * @hd: Pointer to a SCSI HOST structure
3938 * @vtarget: per device private data
3939 * @lun: lun
3940 *
3941 * Uses the ISR, but with special processing.
3942 * MUST be single-threaded.
3943 *
3944 */
3945static void
3946mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
3947{
3948 INTERNAL_CMD iocmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949
3950 /* Following parameters will not change
3951 * in this routine.
3952 */
3953 iocmd.cmd = SYNCHRONIZE_CACHE;
3954 iocmd.flags = 0;
3955 iocmd.physDiskNum = -1;
3956 iocmd.data = NULL;
3957 iocmd.data_dma = -1;
3958 iocmd.size = 0;
3959 iocmd.rsvd = iocmd.rsvd2 = 0;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003960 iocmd.bus = vdevice->bus_id;
3961 iocmd.id = vdevice->target_id;
3962 iocmd.lun = (u8)vdevice->lun;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003963
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003964 if ((vdevice->vtarget->type & TYPE_DISK) &&
3965 (vdevice->configured_lun))
3966 mptscsih_do_cmd(hd, &iocmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003967}
3968
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07003969/* Search IOC page 3 to determine if this is hidden physical disk
3970 */
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07003971static int
3972mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
3973{
3974 int i;
3975
3976 if (!ioc->raid_data.isRaid || !ioc->raid_data.pIocPg3)
3977 return 0;
3978
3979 for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
3980 if (id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID)
3981 return 1;
3982 }
3983
3984 return 0;
3985}
3986
Linus Torvalds1da177e2005-04-16 15:20:36 -07003987#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3988/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3989/**
3990 * mptscsih_domainValidation - Top level handler for domain validation.
3991 * @hd: Pointer to MPT_SCSI_HOST structure.
3992 *
3993 * Uses the ISR, but with special processing.
3994 * Called from schedule, should not be in interrupt mode.
3995 * While thread alive, do dv for all devices needing dv
3996 *
3997 * Return: None.
3998 */
3999static void
4000mptscsih_domainValidation(void *arg)
4001{
4002 MPT_SCSI_HOST *hd;
4003 MPT_ADAPTER *ioc;
4004 unsigned long flags;
4005 int id, maxid, dvStatus, did;
4006 int ii, isPhysDisk;
4007
4008 spin_lock_irqsave(&dvtaskQ_lock, flags);
4009 dvtaskQ_active = 1;
4010 if (dvtaskQ_release) {
4011 dvtaskQ_active = 0;
4012 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4013 return;
4014 }
4015 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4016
4017 /* For this ioc, loop through all devices and do dv to each device.
4018 * When complete with this ioc, search through the ioc list, and
4019 * for each scsi ioc found, do dv for all devices. Exit when no
4020 * device needs dv.
4021 */
4022 did = 1;
4023 while (did) {
4024 did = 0;
4025 list_for_each_entry(ioc, &ioc_list, list) {
4026 spin_lock_irqsave(&dvtaskQ_lock, flags);
4027 if (dvtaskQ_release) {
4028 dvtaskQ_active = 0;
4029 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4030 return;
4031 }
4032 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4033
4034 msleep(250);
4035
Moore, Eric Deana9b29372005-11-16 18:54:20 -07004036 /* DV only to SPI adapters */
4037 if (ioc->bus_type != SPI)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004038 continue;
4039
4040 /* Make sure everything looks ok */
4041 if (ioc->sh == NULL)
4042 continue;
4043
4044 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
4045 if (hd == NULL)
4046 continue;
4047
4048 if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) {
4049 mpt_read_ioc_pg_3(ioc);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004050 if (ioc->raid_data.pIocPg3) {
4051 Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
4052 int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004053
4054 while (numPDisk) {
4055 if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE)
4056 ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
4057
4058 pPDisk++;
4059 numPDisk--;
4060 }
4061 }
4062 ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3;
4063 }
4064
4065 maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
4066
4067 for (id = 0; id < maxid; id++) {
4068 spin_lock_irqsave(&dvtaskQ_lock, flags);
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 dvStatus = hd->ioc->spi_data.dvStatus[id];
4076
4077 if (dvStatus & MPT_SCSICFG_NEED_DV) {
4078 did++;
4079 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING;
4080 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV;
4081
4082 msleep(250);
4083
4084 /* If hidden phys disk, block IO's to all
4085 * raid volumes
4086 * else, process normally
4087 */
4088 isPhysDisk = mptscsih_is_phys_disk(ioc, id);
4089 if (isPhysDisk) {
4090 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004091 if (hd->ioc->raid_data.isRaid & (1 << ii)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004092 hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING;
4093 }
4094 }
4095 }
4096
Moore, Eric Dean2a238ea2005-12-01 10:50:32 -07004097 if(mpt_alt_ioc_wait(hd->ioc)!=0) {
4098 ddvprintk((MYIOC_s_WARN_FMT "alt_ioc busy!\n",
4099 hd->ioc->name));
4100 continue;
4101 }
4102
Linus Torvalds1da177e2005-04-16 15:20:36 -07004103 if (mptscsih_doDv(hd, 0, id) == 1) {
4104 /* Untagged device was busy, try again
4105 */
4106 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV;
4107 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING;
4108 } else {
4109 /* DV is complete. Clear flags.
4110 */
4111 hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING);
4112 }
4113
Moore, Eric Dean2a238ea2005-12-01 10:50:32 -07004114 spin_lock(&hd->ioc->initializing_hba_lock);
4115 hd->ioc->initializing_hba_lock_flag=0;
4116 spin_unlock(&hd->ioc->initializing_hba_lock);
4117
Linus Torvalds1da177e2005-04-16 15:20:36 -07004118 if (isPhysDisk) {
4119 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004120 if (hd->ioc->raid_data.isRaid & (1 << ii)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121 hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING;
4122 }
4123 }
4124 }
4125
4126 if (hd->ioc->spi_data.noQas)
4127 mptscsih_qas_check(hd, id);
4128 }
4129 }
4130 }
4131 }
4132
4133 spin_lock_irqsave(&dvtaskQ_lock, flags);
4134 dvtaskQ_active = 0;
4135 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4136
4137 return;
4138}
4139
Linus Torvalds1da177e2005-04-16 15:20:36 -07004140/* Write SDP1 if no QAS has been enabled
4141 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004142static void
4143mptscsih_qas_check(MPT_SCSI_HOST *hd, int id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004144{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004145 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004146 int ii;
4147
4148 if (hd->Targets == NULL)
4149 return;
4150
4151 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4152 if (ii == id)
4153 continue;
4154
4155 if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0)
4156 continue;
4157
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004158 vtarget = hd->Targets[ii];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004159
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004160 if ((vtarget != NULL) && (!vtarget->raidVolume)) {
4161 if ((vtarget->negoFlags & hd->ioc->spi_data.noQas) == 0) {
4162 vtarget->negoFlags |= hd->ioc->spi_data.noQas;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 dnegoprintk(("writeSDP1: id=%d flags=0\n", id));
4164 mptscsih_writeSDP1(hd, 0, ii, 0);
4165 }
4166 } else {
4167 if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) {
4168 dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id));
4169 mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM);
4170 }
4171 }
4172 }
4173 return;
4174}
4175
4176
4177
4178#define MPT_GET_NVRAM_VALS 0x01
4179#define MPT_UPDATE_MAX 0x02
4180#define MPT_SET_MAX 0x04
4181#define MPT_SET_MIN 0x08
4182#define MPT_FALLBACK 0x10
4183#define MPT_SAVE 0x20
4184
4185/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4186/**
4187 * mptscsih_doDv - Perform domain validation to a target.
4188 * @hd: Pointer to MPT_SCSI_HOST structure.
4189 * @portnum: IOC port number.
4190 * @target: Physical ID of this target
4191 *
4192 * Uses the ISR, but with special processing.
4193 * MUST be single-threaded.
4194 * Test will exit if target is at async & narrow.
4195 *
4196 * Return: None.
4197 */
4198static int
4199mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id)
4200{
4201 MPT_ADAPTER *ioc = hd->ioc;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004202 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004203 SCSIDevicePage1_t *pcfg1Data;
4204 SCSIDevicePage0_t *pcfg0Data;
4205 u8 *pbuf1;
4206 u8 *pbuf2;
4207 u8 *pDvBuf;
4208 dma_addr_t dvbuf_dma = -1;
4209 dma_addr_t buf1_dma = -1;
4210 dma_addr_t buf2_dma = -1;
4211 dma_addr_t cfg1_dma_addr = -1;
4212 dma_addr_t cfg0_dma_addr = -1;
4213 ConfigPageHeader_t header1;
4214 ConfigPageHeader_t header0;
4215 DVPARAMETERS dv;
4216 INTERNAL_CMD iocmd;
4217 CONFIGPARMS cfg;
4218 int dv_alloc = 0;
4219 int rc, sz = 0;
4220 int bufsize = 0;
4221 int dataBufSize = 0;
4222 int echoBufSize = 0;
4223 int notDone;
4224 int patt;
4225 int repeat;
4226 int retcode = 0;
4227 int nfactor = MPT_ULTRA320;
4228 char firstPass = 1;
4229 char doFallback = 0;
4230 char readPage0;
4231 char bus, lun;
4232 char inq0 = 0;
4233
4234 if (ioc->spi_data.sdp1length == 0)
4235 return 0;
4236
4237 if (ioc->spi_data.sdp0length == 0)
4238 return 0;
4239
4240 /* If multiple buses are used, require that the initiator
4241 * id be the same on all buses.
4242 */
4243 if (id == ioc->pfacts[0].PortSCSIID)
4244 return 0;
4245
4246 lun = 0;
4247 bus = (u8) bus_number;
4248 ddvtprintk((MYIOC_s_NOTE_FMT
4249 "DV started: bus=%d, id=%d dv @ %p\n",
4250 ioc->name, bus, id, &dv));
4251
4252 /* Prep DV structure
4253 */
4254 memset (&dv, 0, sizeof(DVPARAMETERS));
4255 dv.id = id;
4256
4257 /* Populate tmax with the current maximum
4258 * transfer parameters for this target.
4259 * Exit if narrow and async.
4260 */
4261 dv.cmd = MPT_GET_NVRAM_VALS;
4262 mptscsih_dv_parms(hd, &dv, NULL);
4263
4264 /* Prep SCSI IO structure
4265 */
4266 iocmd.id = id;
4267 iocmd.bus = bus;
4268 iocmd.lun = lun;
4269 iocmd.flags = 0;
4270 iocmd.physDiskNum = -1;
4271 iocmd.rsvd = iocmd.rsvd2 = 0;
4272
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004273 vtarget = hd->Targets[id];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004274
4275 /* Use tagged commands if possible.
4276 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004277 if (vtarget) {
4278 if (vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004279 iocmd.flags |= MPT_ICFLAG_TAGGED_CMD;
4280 else {
4281 if (hd->ioc->facts.FWVersion.Word < 0x01000600)
4282 return 0;
4283
4284 if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4285 (hd->ioc->facts.FWVersion.Word < 0x01010B00))
4286 return 0;
4287 }
4288 }
4289
4290 /* Prep cfg structure
4291 */
4292 cfg.pageAddr = (bus<<8) | id;
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004293 cfg.cfghdr.hdr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004294
4295 /* Prep SDP0 header
4296 */
4297 header0.PageVersion = ioc->spi_data.sdp0version;
4298 header0.PageLength = ioc->spi_data.sdp0length;
4299 header0.PageNumber = 0;
4300 header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4301
4302 /* Prep SDP1 header
4303 */
4304 header1.PageVersion = ioc->spi_data.sdp1version;
4305 header1.PageLength = ioc->spi_data.sdp1length;
4306 header1.PageNumber = 1;
4307 header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4308
4309 if (header0.PageLength & 1)
4310 dv_alloc = (header0.PageLength * 4) + 4;
4311
4312 dv_alloc += (2048 + (header1.PageLength * 4));
4313
4314 pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma);
4315 if (pDvBuf == NULL)
4316 return 0;
4317
4318 sz = 0;
4319 pbuf1 = (u8 *)pDvBuf;
4320 buf1_dma = dvbuf_dma;
4321 sz +=1024;
4322
4323 pbuf2 = (u8 *) (pDvBuf + sz);
4324 buf2_dma = dvbuf_dma + sz;
4325 sz +=1024;
4326
4327 pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz);
4328 cfg0_dma_addr = dvbuf_dma + sz;
4329 sz += header0.PageLength * 4;
4330
4331 /* 8-byte alignment
4332 */
4333 if (header0.PageLength & 1)
4334 sz += 4;
4335
4336 pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz);
4337 cfg1_dma_addr = dvbuf_dma + sz;
4338
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004339 /* Skip this ID? Set cfg.cfghdr.hdr to force config page write
Linus Torvalds1da177e2005-04-16 15:20:36 -07004340 */
4341 {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004342 SpiCfgData *pspi_data = &hd->ioc->spi_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004343 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
4344 /* Set the factor from nvram */
4345 nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8;
4346 if (nfactor < pspi_data->minSyncFactor )
4347 nfactor = pspi_data->minSyncFactor;
4348
4349 if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE) ||
4350 (pspi_data->PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV) ) {
4351
4352 ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
4353 ioc->name, bus, id, lun));
4354
4355 dv.cmd = MPT_SET_MAX;
4356 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004357 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004358
4359 /* Save the final negotiated settings to
4360 * SCSI device page 1.
4361 */
4362 cfg.physAddr = cfg1_dma_addr;
4363 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4364 cfg.dir = 1;
4365 mpt_config(hd->ioc, &cfg);
4366 goto target_done;
4367 }
4368 }
4369 }
4370
4371 /* Finish iocmd inititialization - hidden or visible disk? */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004372 if (ioc->raid_data.pIocPg3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004373 /* Search IOC page 3 for matching id
4374 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004375 Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
4376 int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004377
4378 while (numPDisk) {
4379 if (pPDisk->PhysDiskID == id) {
4380 /* match */
4381 iocmd.flags |= MPT_ICFLAG_PHYS_DISK;
4382 iocmd.physDiskNum = pPDisk->PhysDiskNum;
4383
4384 /* Quiesce the IM
4385 */
4386 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) {
4387 ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
4388 goto target_done;
4389 }
4390 break;
4391 }
4392 pPDisk++;
4393 numPDisk--;
4394 }
4395 }
4396
4397 /* RAID Volume ID's may double for a physical device. If RAID but
4398 * not a physical ID as well, skip DV.
4399 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004400 if ((hd->ioc->raid_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004401 goto target_done;
4402
4403
4404 /* Basic Test.
4405 * Async & Narrow - Inquiry
4406 * Async & Narrow - Inquiry
4407 * Maximum transfer rate - Inquiry
4408 * Compare buffers:
4409 * If compare, test complete.
4410 * If miscompare and first pass, repeat
4411 * If miscompare and not first pass, fall back and repeat
4412 */
4413 hd->pLocal = NULL;
4414 readPage0 = 0;
4415 sz = SCSI_MAX_INQUIRY_BYTES;
4416 rc = MPT_SCANDV_GOOD;
4417 while (1) {
4418 ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id));
4419 retcode = 0;
4420 dv.cmd = MPT_SET_MIN;
4421 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4422
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004423 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004424 cfg.physAddr = cfg1_dma_addr;
4425 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4426 cfg.dir = 1;
4427 if (mpt_config(hd->ioc, &cfg) != 0)
4428 goto target_done;
4429
4430 /* Wide - narrow - wide workaround case
4431 */
4432 if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
4433 /* Send an untagged command to reset disk Qs corrupted
4434 * when a parity error occurs on a Request Sense.
4435 */
4436 if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
4437 ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4438 (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) {
4439
4440 iocmd.cmd = REQUEST_SENSE;
4441 iocmd.data_dma = buf1_dma;
4442 iocmd.data = pbuf1;
4443 iocmd.size = 0x12;
4444 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4445 goto target_done;
4446 else {
4447 if (hd->pLocal == NULL)
4448 goto target_done;
4449 rc = hd->pLocal->completion;
4450 if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) {
4451 dv.max.width = 0;
4452 doFallback = 0;
4453 } else
4454 goto target_done;
4455 }
4456 } else
4457 goto target_done;
4458 }
4459
4460 iocmd.cmd = INQUIRY;
4461 iocmd.data_dma = buf1_dma;
4462 iocmd.data = pbuf1;
4463 iocmd.size = sz;
4464 memset(pbuf1, 0x00, sz);
4465 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4466 goto target_done;
4467 else {
4468 if (hd->pLocal == NULL)
4469 goto target_done;
4470 rc = hd->pLocal->completion;
4471 if (rc == MPT_SCANDV_GOOD) {
4472 if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) {
4473 if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
4474 retcode = 1;
4475 else
4476 retcode = 0;
4477
4478 goto target_done;
4479 }
4480 } else if (rc == MPT_SCANDV_SENSE) {
4481 ;
4482 } else {
4483 /* If first command doesn't complete
4484 * with a good status or with a check condition,
4485 * exit.
4486 */
4487 goto target_done;
4488 }
4489 }
4490
4491 /* Reset the size for disks
4492 */
4493 inq0 = (*pbuf1) & 0x1F;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004494 if ((inq0 == 0) && vtarget && !vtarget->raidVolume) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004495 sz = 0x40;
4496 iocmd.size = sz;
4497 }
4498
4499 /* Another GEM workaround. Check peripheral device type,
4500 * if PROCESSOR, quit DV.
4501 */
4502 if (inq0 == TYPE_PROCESSOR) {
4503 mptscsih_initTarget(hd,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004504 vtarget,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004505 lun,
4506 pbuf1,
4507 sz);
4508 goto target_done;
4509 }
4510
4511 if (inq0 > 0x08)
4512 goto target_done;
4513
4514 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4515 goto target_done;
4516
4517 if (sz == 0x40) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004518 if ((vtarget->maxWidth == 1) && (vtarget->maxOffset) && (nfactor < 0x0A)
4519 && (vtarget->minSyncFactor > 0x09)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004520 if ((pbuf1[56] & 0x04) == 0)
4521 ;
4522 else if ((pbuf1[56] & 0x01) == 1) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004523 vtarget->minSyncFactor =
Linus Torvalds1da177e2005-04-16 15:20:36 -07004524 nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320;
4525 } else {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004526 vtarget->minSyncFactor =
Linus Torvalds1da177e2005-04-16 15:20:36 -07004527 nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160;
4528 }
4529
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004530 dv.max.factor = vtarget->minSyncFactor;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004531
4532 if ((pbuf1[56] & 0x02) == 0) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004533 vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004534 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004535 ddvprintk((MYIOC_s_NOTE_FMT
4536 "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004537 ioc->name, id, pbuf1[56]));
4538 }
4539 }
4540 }
4541
4542 if (doFallback)
4543 dv.cmd = MPT_FALLBACK;
4544 else
4545 dv.cmd = MPT_SET_MAX;
4546
4547 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4548 if (mpt_config(hd->ioc, &cfg) != 0)
4549 goto target_done;
4550
4551 if ((!dv.now.width) && (!dv.now.offset))
4552 goto target_done;
4553
4554 iocmd.cmd = INQUIRY;
4555 iocmd.data_dma = buf2_dma;
4556 iocmd.data = pbuf2;
4557 iocmd.size = sz;
4558 memset(pbuf2, 0x00, sz);
4559 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4560 goto target_done;
4561 else if (hd->pLocal == NULL)
4562 goto target_done;
4563 else {
4564 /* Save the return code.
4565 * If this is the first pass,
4566 * read SCSI Device Page 0
4567 * and update the target max parameters.
4568 */
4569 rc = hd->pLocal->completion;
4570 doFallback = 0;
4571 if (rc == MPT_SCANDV_GOOD) {
4572 if (!readPage0) {
4573 u32 sdp0_info;
4574 u32 sdp0_nego;
4575
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004576 cfg.cfghdr.hdr = &header0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004577 cfg.physAddr = cfg0_dma_addr;
4578 cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
4579 cfg.dir = 0;
4580
4581 if (mpt_config(hd->ioc, &cfg) != 0)
4582 goto target_done;
4583
4584 sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E;
4585 sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8;
4586
4587 /* Quantum and Fujitsu workarounds.
4588 * Quantum: PPR U320 -> PPR reply with Ultra2 and wide
4589 * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
4590 * Resetart with a request for U160.
4591 */
4592 if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
4593 doFallback = 1;
4594 } else {
4595 dv.cmd = MPT_UPDATE_MAX;
4596 mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data);
4597 /* Update the SCSI device page 1 area
4598 */
4599 pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters;
4600 readPage0 = 1;
4601 }
4602 }
4603
4604 /* Quantum workaround. Restart this test will the fallback
4605 * flag set.
4606 */
4607 if (doFallback == 0) {
4608 if (memcmp(pbuf1, pbuf2, sz) != 0) {
4609 if (!firstPass)
4610 doFallback = 1;
4611 } else {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004612 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07004613 "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id));
4614 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE;
4615 mptscsih_initTarget(hd,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004616 vtarget,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004617 lun,
4618 pbuf1,
4619 sz);
4620 break; /* test complete */
4621 }
4622 }
4623
4624
4625 } else if (rc == MPT_SCANDV_ISSUE_SENSE)
4626 doFallback = 1; /* set fallback flag */
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004627 else if ((rc == MPT_SCANDV_DID_RESET) ||
4628 (rc == MPT_SCANDV_SENSE) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004629 (rc == MPT_SCANDV_FALLBACK))
4630 doFallback = 1; /* set fallback flag */
4631 else
4632 goto target_done;
4633
4634 firstPass = 0;
4635 }
4636 }
4637 ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id));
4638
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004639 if (ioc->spi_data.mpt_dv == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004640 goto target_done;
4641
4642 inq0 = (*pbuf1) & 0x1F;
4643
4644 /* Continue only for disks
4645 */
4646 if (inq0 != 0)
4647 goto target_done;
4648
4649 if ( ioc->spi_data.PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY )
4650 goto target_done;
4651
4652 /* Start the Enhanced Test.
4653 * 0) issue TUR to clear out check conditions
4654 * 1) read capacity of echo (regular) buffer
4655 * 2) reserve device
4656 * 3) do write-read-compare data pattern test
4657 * 4) release
4658 * 5) update nego parms to target struct
4659 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004660 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004661 cfg.physAddr = cfg1_dma_addr;
4662 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4663 cfg.dir = 1;
4664
4665 iocmd.cmd = TEST_UNIT_READY;
4666 iocmd.data_dma = -1;
4667 iocmd.data = NULL;
4668 iocmd.size = 0;
4669 notDone = 1;
4670 while (notDone) {
4671 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4672 goto target_done;
4673
4674 if (hd->pLocal == NULL)
4675 goto target_done;
4676
4677 rc = hd->pLocal->completion;
4678 if (rc == MPT_SCANDV_GOOD)
4679 notDone = 0;
4680 else if (rc == MPT_SCANDV_SENSE) {
4681 u8 skey = hd->pLocal->sense[2] & 0x0F;
4682 u8 asc = hd->pLocal->sense[12];
4683 u8 ascq = hd->pLocal->sense[13];
4684 ddvprintk((MYIOC_s_INFO_FMT
4685 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4686 ioc->name, skey, asc, ascq));
4687
4688 if (skey == UNIT_ATTENTION)
4689 notDone++; /* repeat */
4690 else if ((skey == NOT_READY) &&
4691 (asc == 0x04)&&(ascq == 0x01)) {
4692 /* wait then repeat */
4693 mdelay (2000);
4694 notDone++;
4695 } else if ((skey == NOT_READY) && (asc == 0x3A)) {
4696 /* no medium, try read test anyway */
4697 notDone = 0;
4698 } else {
4699 /* All other errors are fatal.
4700 */
4701 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4702 ioc->name));
4703 goto target_done;
4704 }
4705 } else
4706 goto target_done;
4707 }
4708
4709 iocmd.cmd = READ_BUFFER;
4710 iocmd.data_dma = buf1_dma;
4711 iocmd.data = pbuf1;
4712 iocmd.size = 4;
4713 iocmd.flags |= MPT_ICFLAG_BUF_CAP;
4714
4715 dataBufSize = 0;
4716 echoBufSize = 0;
4717 for (patt = 0; patt < 2; patt++) {
4718 if (patt == 0)
4719 iocmd.flags |= MPT_ICFLAG_ECHO;
4720 else
4721 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4722
4723 notDone = 1;
4724 while (notDone) {
4725 bufsize = 0;
4726
4727 /* If not ready after 8 trials,
4728 * give up on this device.
4729 */
4730 if (notDone > 8)
4731 goto target_done;
4732
4733 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4734 goto target_done;
4735 else if (hd->pLocal == NULL)
4736 goto target_done;
4737 else {
4738 rc = hd->pLocal->completion;
4739 ddvprintk(("ReadBuffer Comp Code %d", rc));
4740 ddvprintk((" buff: %0x %0x %0x %0x\n",
4741 pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3]));
4742
4743 if (rc == MPT_SCANDV_GOOD) {
4744 notDone = 0;
4745 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4746 bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3];
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004747 if (pbuf1[0] & 0x01)
4748 iocmd.flags |= MPT_ICFLAG_EBOS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004749 } else {
4750 bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3];
4751 }
4752 } else if (rc == MPT_SCANDV_SENSE) {
4753 u8 skey = hd->pLocal->sense[2] & 0x0F;
4754 u8 asc = hd->pLocal->sense[12];
4755 u8 ascq = hd->pLocal->sense[13];
4756 ddvprintk((MYIOC_s_INFO_FMT
4757 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4758 ioc->name, skey, asc, ascq));
4759 if (skey == ILLEGAL_REQUEST) {
4760 notDone = 0;
4761 } else if (skey == UNIT_ATTENTION) {
4762 notDone++; /* repeat */
4763 } else if ((skey == NOT_READY) &&
4764 (asc == 0x04)&&(ascq == 0x01)) {
4765 /* wait then repeat */
4766 mdelay (2000);
4767 notDone++;
4768 } else {
4769 /* All other errors are fatal.
4770 */
4771 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4772 ioc->name));
4773 goto target_done;
4774 }
4775 } else {
4776 /* All other errors are fatal
4777 */
4778 goto target_done;
4779 }
4780 }
4781 }
4782
4783 if (iocmd.flags & MPT_ICFLAG_ECHO)
4784 echoBufSize = bufsize;
4785 else
4786 dataBufSize = bufsize;
4787 }
4788 sz = 0;
4789 iocmd.flags &= ~MPT_ICFLAG_BUF_CAP;
4790
4791 /* Use echo buffers if possible,
4792 * Exit if both buffers are 0.
4793 */
4794 if (echoBufSize > 0) {
4795 iocmd.flags |= MPT_ICFLAG_ECHO;
4796 if (dataBufSize > 0)
4797 bufsize = min(echoBufSize, dataBufSize);
4798 else
4799 bufsize = echoBufSize;
4800 } else if (dataBufSize == 0)
4801 goto target_done;
4802
4803 ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name,
4804 (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize));
4805
4806 /* Data buffers for write-read-compare test max 1K.
4807 */
4808 sz = min(bufsize, 1024);
4809
4810 /* --- loop ----
4811 * On first pass, always issue a reserve.
4812 * On additional loops, only if a reset has occurred.
4813 * iocmd.flags indicates if echo or regular buffer
4814 */
4815 for (patt = 0; patt < 4; patt++) {
4816 ddvprintk(("Pattern %d\n", patt));
4817 if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) {
4818 iocmd.cmd = TEST_UNIT_READY;
4819 iocmd.data_dma = -1;
4820 iocmd.data = NULL;
4821 iocmd.size = 0;
4822 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4823 goto target_done;
4824
4825 iocmd.cmd = RELEASE;
4826 iocmd.data_dma = -1;
4827 iocmd.data = NULL;
4828 iocmd.size = 0;
4829 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4830 goto target_done;
4831 else if (hd->pLocal == NULL)
4832 goto target_done;
4833 else {
4834 rc = hd->pLocal->completion;
4835 ddvprintk(("Release rc %d\n", rc));
4836 if (rc == MPT_SCANDV_GOOD)
4837 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4838 else
4839 goto target_done;
4840 }
4841 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4842 }
4843 iocmd.flags &= ~MPT_ICFLAG_DID_RESET;
4844
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004845 if (iocmd.flags & MPT_ICFLAG_EBOS)
4846 goto skip_Reserve;
4847
Linus Torvalds1da177e2005-04-16 15:20:36 -07004848 repeat = 5;
4849 while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) {
4850 iocmd.cmd = RESERVE;
4851 iocmd.data_dma = -1;
4852 iocmd.data = NULL;
4853 iocmd.size = 0;
4854 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4855 goto target_done;
4856 else if (hd->pLocal == NULL)
4857 goto target_done;
4858 else {
4859 rc = hd->pLocal->completion;
4860 if (rc == MPT_SCANDV_GOOD) {
4861 iocmd.flags |= MPT_ICFLAG_RESERVED;
4862 } else if (rc == MPT_SCANDV_SENSE) {
4863 /* Wait if coming ready
4864 */
4865 u8 skey = hd->pLocal->sense[2] & 0x0F;
4866 u8 asc = hd->pLocal->sense[12];
4867 u8 ascq = hd->pLocal->sense[13];
4868 ddvprintk((MYIOC_s_INFO_FMT
4869 "DV: Reserve Failed: ", ioc->name));
4870 ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4871 skey, asc, ascq));
4872
4873 if ((skey == NOT_READY) && (asc == 0x04)&&
4874 (ascq == 0x01)) {
4875 /* wait then repeat */
4876 mdelay (2000);
4877 notDone++;
4878 } else {
4879 ddvprintk((MYIOC_s_INFO_FMT
4880 "DV: Reserved Failed.", ioc->name));
4881 goto target_done;
4882 }
4883 } else {
4884 ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.",
4885 ioc->name));
4886 goto target_done;
4887 }
4888 }
4889 }
4890
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004891skip_Reserve:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004892 mptscsih_fillbuf(pbuf1, sz, patt, 1);
4893 iocmd.cmd = WRITE_BUFFER;
4894 iocmd.data_dma = buf1_dma;
4895 iocmd.data = pbuf1;
4896 iocmd.size = sz;
4897 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4898 goto target_done;
4899 else if (hd->pLocal == NULL)
4900 goto target_done;
4901 else {
4902 rc = hd->pLocal->completion;
4903 if (rc == MPT_SCANDV_GOOD)
4904 ; /* Issue read buffer */
4905 else if (rc == MPT_SCANDV_DID_RESET) {
4906 /* If using echo buffers, reset to data buffers.
4907 * Else do Fallback and restart
4908 * this test (re-issue reserve
4909 * because of bus reset).
4910 */
4911 if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) {
4912 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4913 } else {
4914 dv.cmd = MPT_FALLBACK;
4915 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4916
4917 if (mpt_config(hd->ioc, &cfg) != 0)
4918 goto target_done;
4919
4920 if ((!dv.now.width) && (!dv.now.offset))
4921 goto target_done;
4922 }
4923
4924 iocmd.flags |= MPT_ICFLAG_DID_RESET;
4925 patt = -1;
4926 continue;
4927 } else if (rc == MPT_SCANDV_SENSE) {
4928 /* Restart data test if UA, else quit.
4929 */
4930 u8 skey = hd->pLocal->sense[2] & 0x0F;
4931 ddvprintk((MYIOC_s_INFO_FMT
4932 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
4933 hd->pLocal->sense[12], hd->pLocal->sense[13]));
4934 if (skey == UNIT_ATTENTION) {
4935 patt = -1;
4936 continue;
4937 } else if (skey == ILLEGAL_REQUEST) {
4938 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4939 if (dataBufSize >= bufsize) {
4940 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4941 patt = -1;
4942 continue;
4943 }
4944 }
4945 goto target_done;
4946 }
4947 else
4948 goto target_done;
4949 } else {
4950 /* fatal error */
4951 goto target_done;
4952 }
4953 }
4954
4955 iocmd.cmd = READ_BUFFER;
4956 iocmd.data_dma = buf2_dma;
4957 iocmd.data = pbuf2;
4958 iocmd.size = sz;
4959 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4960 goto target_done;
4961 else if (hd->pLocal == NULL)
4962 goto target_done;
4963 else {
4964 rc = hd->pLocal->completion;
4965 if (rc == MPT_SCANDV_GOOD) {
4966 /* If buffers compare,
4967 * go to next pattern,
4968 * else, do a fallback and restart
4969 * data transfer test.
4970 */
4971 if (memcmp (pbuf1, pbuf2, sz) == 0) {
4972 ; /* goto next pattern */
4973 } else {
4974 /* Miscompare with Echo buffer, go to data buffer,
4975 * if that buffer exists.
4976 * Miscompare with Data buffer, check first 4 bytes,
4977 * some devices return capacity. Exit in this case.
4978 */
4979 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4980 if (dataBufSize >= bufsize)
4981 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4982 else
4983 goto target_done;
4984 } else {
4985 if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) {
4986 /* Argh. Device returning wrong data.
4987 * Quit DV for this device.
4988 */
4989 goto target_done;
4990 }
4991
4992 /* Had an actual miscompare. Slow down.*/
4993 dv.cmd = MPT_FALLBACK;
4994 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4995
4996 if (mpt_config(hd->ioc, &cfg) != 0)
4997 goto target_done;
4998
4999 if ((!dv.now.width) && (!dv.now.offset))
5000 goto target_done;
5001 }
5002
5003 patt = -1;
5004 continue;
5005 }
5006 } else if (rc == MPT_SCANDV_DID_RESET) {
5007 /* Do Fallback and restart
5008 * this test (re-issue reserve
5009 * because of bus reset).
5010 */
5011 dv.cmd = MPT_FALLBACK;
5012 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5013
5014 if (mpt_config(hd->ioc, &cfg) != 0)
5015 goto target_done;
5016
5017 if ((!dv.now.width) && (!dv.now.offset))
5018 goto target_done;
5019
5020 iocmd.flags |= MPT_ICFLAG_DID_RESET;
5021 patt = -1;
5022 continue;
5023 } else if (rc == MPT_SCANDV_SENSE) {
5024 /* Restart data test if UA, else quit.
5025 */
5026 u8 skey = hd->pLocal->sense[2] & 0x0F;
5027 ddvprintk((MYIOC_s_INFO_FMT
5028 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
5029 hd->pLocal->sense[12], hd->pLocal->sense[13]));
5030 if (skey == UNIT_ATTENTION) {
5031 patt = -1;
5032 continue;
5033 }
5034 else
5035 goto target_done;
5036 } else {
5037 /* fatal error */
5038 goto target_done;
5039 }
5040 }
5041
5042 } /* --- end of patt loop ---- */
5043
5044target_done:
5045 if (iocmd.flags & MPT_ICFLAG_RESERVED) {
5046 iocmd.cmd = RELEASE;
5047 iocmd.data_dma = -1;
5048 iocmd.data = NULL;
5049 iocmd.size = 0;
5050 if (mptscsih_do_cmd(hd, &iocmd) < 0)
5051 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5052 ioc->name, id);
5053 else if (hd->pLocal) {
5054 if (hd->pLocal->completion == MPT_SCANDV_GOOD)
5055 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
5056 } else {
5057 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5058 ioc->name, id);
5059 }
5060 }
5061
5062
5063 /* Set if cfg1_dma_addr contents is valid
5064 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005065 if ((cfg.cfghdr.hdr != NULL) && (retcode == 0)){
Linus Torvalds1da177e2005-04-16 15:20:36 -07005066 /* If disk, not U320, disable QAS
5067 */
5068 if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) {
5069 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02005070 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07005071 "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor));
5072 }
5073
5074 dv.cmd = MPT_SAVE;
5075 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5076
5077 /* Double writes to SDP1 can cause problems,
5078 * skip save of the final negotiated settings to
5079 * SCSI device page 1.
5080 *
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005081 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005082 cfg.physAddr = cfg1_dma_addr;
5083 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
5084 cfg.dir = 1;
5085 mpt_config(hd->ioc, &cfg);
5086 */
5087 }
5088
5089 /* If this is a RAID Passthrough, enable internal IOs
5090 */
5091 if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) {
5092 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0)
5093 ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name));
5094 }
5095
5096 /* Done with the DV scan of the current target
5097 */
5098 if (pDvBuf)
5099 pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma);
5100
5101 ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n",
5102 ioc->name, id));
5103
5104 return retcode;
5105}
5106
5107/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5108/* mptscsih_dv_parms - perform a variety of operations on the
5109 * parameters used for negotiation.
5110 * @hd: Pointer to a SCSI host.
5111 * @dv: Pointer to a structure that contains the maximum and current
5112 * negotiated parameters.
5113 */
5114static void
5115mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage)
5116{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005117 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005118 SCSIDevicePage0_t *pPage0;
5119 SCSIDevicePage1_t *pPage1;
5120 int val = 0, data, configuration;
5121 u8 width = 0;
5122 u8 offset = 0;
5123 u8 factor = 0;
5124 u8 negoFlags = 0;
5125 u8 cmd = dv->cmd;
5126 u8 id = dv->id;
5127
5128 switch (cmd) {
5129 case MPT_GET_NVRAM_VALS:
5130 ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ",
5131 hd->ioc->name));
5132 /* Get the NVRAM values and save in tmax
5133 * If not an LVD bus, the adapter minSyncFactor has been
5134 * already throttled back.
5135 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06005136 negoFlags = hd->ioc->spi_data.noQas;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005137 if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume) {
5138 width = vtarget->maxWidth;
5139 offset = vtarget->maxOffset;
5140 factor = vtarget->minSyncFactor;
5141 negoFlags |= vtarget->negoFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005142 } else {
5143 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
5144 data = hd->ioc->spi_data.nvram[id];
5145 width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
5146 if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0)
5147 factor = MPT_ASYNC;
5148 else {
5149 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
5150 if ((factor == 0) || (factor == MPT_ASYNC)){
5151 factor = MPT_ASYNC;
5152 offset = 0;
5153 }
5154 }
5155 } else {
5156 width = MPT_NARROW;
5157 offset = 0;
5158 factor = MPT_ASYNC;
5159 }
5160
5161 /* Set the negotiation flags */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005162 if (!width)
5163 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
5164
5165 if (!offset)
5166 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
5167 }
5168
5169 /* limit by adapter capabilities */
5170 width = min(width, hd->ioc->spi_data.maxBusWidth);
5171 offset = min(offset, hd->ioc->spi_data.maxSyncOffset);
5172 factor = max(factor, hd->ioc->spi_data.minSyncFactor);
5173
5174 /* Check Consistency */
5175 if (offset && (factor < MPT_ULTRA2) && !width)
5176 factor = MPT_ULTRA2;
5177
5178 dv->max.width = width;
5179 dv->max.offset = offset;
5180 dv->max.factor = factor;
5181 dv->max.flags = negoFlags;
5182 ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n",
5183 id, width, factor, offset, negoFlags));
5184 break;
5185
5186 case MPT_UPDATE_MAX:
5187 ddvprintk((MYIOC_s_NOTE_FMT
5188 "Updating with SDP0 Data: ", hd->ioc->name));
5189 /* Update tmax values with those from Device Page 0.*/
5190 pPage0 = (SCSIDevicePage0_t *) pPage;
5191 if (pPage0) {
Christoph Hellwig637fa992005-08-18 16:25:44 +02005192 val = le32_to_cpu(pPage0->NegotiatedParameters);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005193 dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0;
5194 dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16;
5195 dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8;
5196 }
5197
5198 dv->now.width = dv->max.width;
5199 dv->now.offset = dv->max.offset;
5200 dv->now.factor = dv->max.factor;
5201 ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n",
5202 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5203 break;
5204
5205 case MPT_SET_MAX:
5206 ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ",
5207 hd->ioc->name));
5208 /* Set current to the max values. Update the config page.*/
5209 dv->now.width = dv->max.width;
5210 dv->now.offset = dv->max.offset;
5211 dv->now.factor = dv->max.factor;
5212 dv->now.flags = dv->max.flags;
5213
5214 pPage1 = (SCSIDevicePage1_t *)pPage;
5215 if (pPage1) {
5216 mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor,
5217 dv->now.offset, &val, &configuration, dv->now.flags);
5218 dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5219 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005220 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005221 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005222 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005223 }
5224
Christoph Hellwig637fa992005-08-18 16:25:44 +02005225 ddvprintk(("id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x configuration=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
5227 break;
5228
5229 case MPT_SET_MIN:
5230 ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
5231 hd->ioc->name));
5232 /* Set page to asynchronous and narrow
5233 * Do not update now, breaks fallback routine. */
5234 width = MPT_NARROW;
5235 offset = 0;
5236 factor = MPT_ASYNC;
5237 negoFlags = dv->max.flags;
5238
5239 pPage1 = (SCSIDevicePage1_t *)pPage;
5240 if (pPage1) {
5241 mptscsih_setDevicePage1Flags (width, factor,
5242 offset, &val, &configuration, negoFlags);
5243 dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5244 id, width, factor, offset, negoFlags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005245 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005246 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005247 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005248 }
5249 ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n",
5250 id, width, factor, offset, val, configuration, negoFlags));
5251 break;
5252
5253 case MPT_FALLBACK:
5254 ddvprintk((MYIOC_s_NOTE_FMT
5255 "Fallback: Start: offset %d, factor %x, width %d \n",
5256 hd->ioc->name, dv->now.offset,
5257 dv->now.factor, dv->now.width));
5258 width = dv->now.width;
5259 offset = dv->now.offset;
5260 factor = dv->now.factor;
5261 if ((offset) && (dv->max.width)) {
5262 if (factor < MPT_ULTRA160)
5263 factor = MPT_ULTRA160;
5264 else if (factor < MPT_ULTRA2) {
5265 factor = MPT_ULTRA2;
5266 width = MPT_WIDE;
5267 } else if ((factor == MPT_ULTRA2) && width) {
5268 factor = MPT_ULTRA2;
5269 width = MPT_NARROW;
5270 } else if (factor < MPT_ULTRA) {
5271 factor = MPT_ULTRA;
5272 width = MPT_WIDE;
5273 } else if ((factor == MPT_ULTRA) && width) {
5274 width = MPT_NARROW;
5275 } else if (factor < MPT_FAST) {
5276 factor = MPT_FAST;
5277 width = MPT_WIDE;
5278 } else if ((factor == MPT_FAST) && width) {
5279 factor = MPT_FAST;
5280 width = MPT_NARROW;
5281 } else if (factor < MPT_SCSI) {
5282 factor = MPT_SCSI;
5283 width = MPT_WIDE;
5284 } else if ((factor == MPT_SCSI) && width) {
5285 factor = MPT_SCSI;
5286 width = MPT_NARROW;
5287 } else {
5288 factor = MPT_ASYNC;
5289 offset = 0;
5290 }
5291
5292 } else if (offset) {
5293 width = MPT_NARROW;
5294 if (factor < MPT_ULTRA)
5295 factor = MPT_ULTRA;
5296 else if (factor < MPT_FAST)
5297 factor = MPT_FAST;
5298 else if (factor < MPT_SCSI)
5299 factor = MPT_SCSI;
5300 else {
5301 factor = MPT_ASYNC;
5302 offset = 0;
5303 }
5304
5305 } else {
5306 width = MPT_NARROW;
5307 factor = MPT_ASYNC;
5308 }
5309 dv->max.flags |= MPT_TARGET_NO_NEGO_QAS;
5310 dv->max.flags &= ~MPT_TAPE_NEGO_IDP;
5311
5312 dv->now.width = width;
5313 dv->now.offset = offset;
5314 dv->now.factor = factor;
5315 dv->now.flags = dv->max.flags;
5316
5317 pPage1 = (SCSIDevicePage1_t *)pPage;
5318 if (pPage1) {
5319 mptscsih_setDevicePage1Flags (width, factor, offset, &val,
5320 &configuration, dv->now.flags);
Christoph Hellwig637fa992005-08-18 16:25:44 +02005321 dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x negoFlags=%x request=%x config=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005322 id, width, offset, factor, dv->now.flags, val, configuration));
5323
Christoph Hellwig637fa992005-08-18 16:25:44 +02005324 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005325 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005326 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005327 }
5328
5329 ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n",
5330 id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration));
5331 break;
5332
5333 case MPT_SAVE:
5334 ddvprintk((MYIOC_s_NOTE_FMT
5335 "Saving to Target structure: ", hd->ioc->name));
5336 ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n",
5337 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5338
5339 /* Save these values to target structures
5340 * or overwrite nvram (phys disks only).
5341 */
5342
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005343 if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume ) {
5344 vtarget->maxWidth = dv->now.width;
5345 vtarget->maxOffset = dv->now.offset;
5346 vtarget->minSyncFactor = dv->now.factor;
5347 vtarget->negoFlags = dv->now.flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005348 } else {
5349 /* Preserv all flags, use
5350 * read-modify-write algorithm
5351 */
5352 if (hd->ioc->spi_data.nvram) {
5353 data = hd->ioc->spi_data.nvram[id];
5354
5355 if (dv->now.width)
5356 data &= ~MPT_NVRAM_WIDE_DISABLE;
5357 else
5358 data |= MPT_NVRAM_WIDE_DISABLE;
5359
5360 if (!dv->now.offset)
5361 factor = MPT_ASYNC;
5362
5363 data &= ~MPT_NVRAM_SYNC_MASK;
5364 data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK;
5365
5366 hd->ioc->spi_data.nvram[id] = data;
5367 }
5368 }
5369 break;
5370 }
5371}
5372
5373/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5374/* mptscsih_fillbuf - fill a buffer with a special data pattern
5375 * cleanup. For bus scan only.
5376 *
5377 * @buffer: Pointer to data buffer to be filled.
5378 * @size: Number of bytes to fill
5379 * @index: Pattern index
5380 * @width: bus width, 0 (8 bits) or 1 (16 bits)
5381 */
5382static void
5383mptscsih_fillbuf(char *buffer, int size, int index, int width)
5384{
5385 char *ptr = buffer;
5386 int ii;
5387 char byte;
5388 short val;
5389
5390 switch (index) {
5391 case 0:
5392
5393 if (width) {
5394 /* Pattern: 0000 FFFF 0000 FFFF
5395 */
5396 for (ii=0; ii < size; ii++, ptr++) {
5397 if (ii & 0x02)
5398 *ptr = 0xFF;
5399 else
5400 *ptr = 0x00;
5401 }
5402 } else {
5403 /* Pattern: 00 FF 00 FF
5404 */
5405 for (ii=0; ii < size; ii++, ptr++) {
5406 if (ii & 0x01)
5407 *ptr = 0xFF;
5408 else
5409 *ptr = 0x00;
5410 }
5411 }
5412 break;
5413
5414 case 1:
5415 if (width) {
5416 /* Pattern: 5555 AAAA 5555 AAAA 5555
5417 */
5418 for (ii=0; ii < size; ii++, ptr++) {
5419 if (ii & 0x02)
5420 *ptr = 0xAA;
5421 else
5422 *ptr = 0x55;
5423 }
5424 } else {
5425 /* Pattern: 55 AA 55 AA 55
5426 */
5427 for (ii=0; ii < size; ii++, ptr++) {
5428 if (ii & 0x01)
5429 *ptr = 0xAA;
5430 else
5431 *ptr = 0x55;
5432 }
5433 }
5434 break;
5435
5436 case 2:
5437 /* Pattern: 00 01 02 03 04 05
5438 * ... FE FF 00 01..
5439 */
5440 for (ii=0; ii < size; ii++, ptr++)
5441 *ptr = (char) ii;
5442 break;
5443
5444 case 3:
5445 if (width) {
5446 /* Wide Pattern: FFFE 0001 FFFD 0002
5447 * ... 4000 DFFF 8000 EFFF
5448 */
5449 byte = 0;
5450 for (ii=0; ii < size/2; ii++) {
5451 /* Create the base pattern
5452 */
5453 val = (1 << byte);
5454 /* every 64 (0x40) bytes flip the pattern
5455 * since we fill 2 bytes / iteration,
5456 * test for ii = 0x20
5457 */
5458 if (ii & 0x20)
5459 val = ~(val);
5460
5461 if (ii & 0x01) {
5462 *ptr = (char)( (val & 0xFF00) >> 8);
5463 ptr++;
5464 *ptr = (char)(val & 0xFF);
5465 byte++;
5466 byte &= 0x0F;
5467 } else {
5468 val = ~val;
5469 *ptr = (char)( (val & 0xFF00) >> 8);
5470 ptr++;
5471 *ptr = (char)(val & 0xFF);
5472 }
5473
5474 ptr++;
5475 }
5476 } else {
5477 /* Narrow Pattern: FE 01 FD 02 FB 04
5478 * .. 7F 80 01 FE 02 FD ... 80 7F
5479 */
5480 byte = 0;
5481 for (ii=0; ii < size; ii++, ptr++) {
5482 /* Base pattern - first 32 bytes
5483 */
5484 if (ii & 0x01) {
5485 *ptr = (1 << byte);
5486 byte++;
5487 byte &= 0x07;
5488 } else {
5489 *ptr = (char) (~(1 << byte));
5490 }
5491
5492 /* Flip the pattern every 32 bytes
5493 */
5494 if (ii & 0x20)
5495 *ptr = ~(*ptr);
5496 }
5497 }
5498 break;
5499 }
5500}
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005501
5502/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5503/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
5504 * Else set the NEED_DV flag after Read Capacity Issued (disks)
5505 * or Mode Sense (cdroms).
5506 *
5507 * Tapes, initTarget will set this flag on completion of Inquiry command.
5508 * Called only if DV_NOT_DONE flag is set
5509 */
5510static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005511mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc)
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005512{
5513 MPT_ADAPTER *ioc = hd->ioc;
5514 u8 cmd;
5515 SpiCfgData *pSpi;
5516
5517 ddvtprintk((MYIOC_s_NOTE_FMT
5518 " set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005519 hd->ioc->name, sc->device->id, sc->device->lun , hd->negoNvram, sc->cmnd[0]));
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005520
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005521 if ((sc->device->lun != 0) || (hd->negoNvram != 0))
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005522 return;
5523
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005524 cmd = sc->cmnd[0];
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005525
5526 if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) {
5527 pSpi = &ioc->spi_data;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005528 if ((ioc->raid_data.isRaid & (1 << sc->device->id)) && ioc->raid_data.pIocPg3) {
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005529 /* Set NEED_DV for all hidden disks
5530 */
5531 Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
5532 int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
5533
5534 while (numPDisk) {
5535 pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
5536 ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
5537 pPDisk++;
5538 numPDisk--;
5539 }
5540 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005541 pSpi->dvStatus[sc->device->id] |= MPT_SCSICFG_NEED_DV;
5542 ddvtprintk(("NEED_DV set for visible disk id %d\n", sc->device->id));
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005543 }
5544}
5545
5546/* mptscsih_raid_set_dv_flags()
5547 *
5548 * New or replaced disk. Set DV flag and schedule DV.
5549 */
5550static void
5551mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id)
5552{
5553 MPT_ADAPTER *ioc = hd->ioc;
5554 SpiCfgData *pSpi = &ioc->spi_data;
5555 Ioc3PhysDisk_t *pPDisk;
5556 int numPDisk;
5557
5558 if (hd->negoNvram != 0)
5559 return;
5560
5561 ddvtprintk(("DV requested for phys disk id %d\n", id));
5562 if (ioc->raid_data.pIocPg3) {
5563 pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
5564 numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
5565 while (numPDisk) {
5566 if (id == pPDisk->PhysDiskNum) {
5567 pSpi->dvStatus[pPDisk->PhysDiskID] =
5568 (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE);
5569 pSpi->forceDv = MPT_SCSICFG_NEED_DV;
5570 ddvtprintk(("NEED_DV set for phys disk id %d\n",
5571 pPDisk->PhysDiskID));
5572 break;
5573 }
5574 pPDisk++;
5575 numPDisk--;
5576 }
5577
5578 if (numPDisk == 0) {
5579 /* The physical disk that needs DV was not found
5580 * in the stored IOC Page 3. The driver must reload
5581 * this page. DV routine will set the NEED_DV flag for
5582 * all phys disks that have DV_NOT_DONE set.
5583 */
5584 pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
5585 ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n",id));
5586 }
5587 }
5588}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005589#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
5590
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005591EXPORT_SYMBOL(mptscsih_remove);
5592EXPORT_SYMBOL(mptscsih_shutdown);
5593#ifdef CONFIG_PM
5594EXPORT_SYMBOL(mptscsih_suspend);
5595EXPORT_SYMBOL(mptscsih_resume);
5596#endif
5597EXPORT_SYMBOL(mptscsih_proc_info);
5598EXPORT_SYMBOL(mptscsih_info);
5599EXPORT_SYMBOL(mptscsih_qcmd);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005600EXPORT_SYMBOL(mptscsih_target_alloc);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005601EXPORT_SYMBOL(mptscsih_slave_alloc);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005602EXPORT_SYMBOL(mptscsih_target_destroy);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005603EXPORT_SYMBOL(mptscsih_slave_destroy);
5604EXPORT_SYMBOL(mptscsih_slave_configure);
5605EXPORT_SYMBOL(mptscsih_abort);
5606EXPORT_SYMBOL(mptscsih_dev_reset);
5607EXPORT_SYMBOL(mptscsih_bus_reset);
5608EXPORT_SYMBOL(mptscsih_host_reset);
5609EXPORT_SYMBOL(mptscsih_bios_param);
5610EXPORT_SYMBOL(mptscsih_io_done);
5611EXPORT_SYMBOL(mptscsih_taskmgmt_complete);
5612EXPORT_SYMBOL(mptscsih_scandv_complete);
5613EXPORT_SYMBOL(mptscsih_event_process);
5614EXPORT_SYMBOL(mptscsih_ioc_reset);
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06005615EXPORT_SYMBOL(mptscsih_change_queue_depth);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005616EXPORT_SYMBOL(mptscsih_timer_expired);
James Bottomley663e1aa2006-01-29 12:10:24 -06005617EXPORT_SYMBOL(mptscsih_TMHandler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005618
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005619/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/