Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/spi-2.6
[linux-2.6.git] / drivers / message / fusion / mptfc.c
1 /*
2  *  linux/drivers/message/fusion/mptfc.c
3  *      For use with LSI Logic PCI chip/adapter(s)
4  *      running LSI Logic Fusion MPT (Message Passing Technology) firmware.
5  *
6  *  Copyright (c) 1999-2005 LSI Logic Corporation
7  *  (mailto:mpt_linux_developer@lsil.com)
8  *
9  */
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 #include "linux_compat.h"       /* linux-2.6 tweaks */
47 #include <linux/module.h>
48 #include <linux/kernel.h>
49 #include <linux/init.h>
50 #include <linux/errno.h>
51 #include <linux/kdev_t.h>
52 #include <linux/blkdev.h>
53 #include <linux/delay.h>        /* for mdelay */
54 #include <linux/interrupt.h>    /* needed for in_interrupt() proto */
55 #include <linux/reboot.h>       /* notifier code */
56 #include <linux/sched.h>
57 #include <linux/workqueue.h>
58
59 #include <scsi/scsi.h>
60 #include <scsi/scsi_cmnd.h>
61 #include <scsi/scsi_device.h>
62 #include <scsi/scsi_host.h>
63 #include <scsi/scsi_tcq.h>
64
65 #include "mptbase.h"
66 #include "mptscsih.h"
67
68 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
69 #define my_NAME         "Fusion MPT FC Host driver"
70 #define my_VERSION      MPT_LINUX_VERSION_COMMON
71 #define MYNAM           "mptfc"
72
73 MODULE_AUTHOR(MODULEAUTHOR);
74 MODULE_DESCRIPTION(my_NAME);
75 MODULE_LICENSE("GPL");
76
77 /* Command line args */
78 static int mpt_pq_filter = 0;
79 module_param(mpt_pq_filter, int, 0);
80 MODULE_PARM_DESC(mpt_pq_filter, " Enable peripheral qualifier filter: enable=1  (default=0)");
81
82 static int      mptfcDoneCtx = -1;
83 static int      mptfcTaskCtx = -1;
84 static int      mptfcInternalCtx = -1; /* Used only for internal commands */
85
86 static struct scsi_host_template mptfc_driver_template = {
87         .module                         = THIS_MODULE,
88         .proc_name                      = "mptfc",
89         .proc_info                      = mptscsih_proc_info,
90         .name                           = "MPT FC Host",
91         .info                           = mptscsih_info,
92         .queuecommand                   = mptscsih_qcmd,
93         .target_alloc                   = mptscsih_target_alloc,
94         .slave_alloc                    = mptscsih_slave_alloc,
95         .slave_configure                = mptscsih_slave_configure,
96         .target_destroy                 = mptscsih_target_destroy,
97         .slave_destroy                  = mptscsih_slave_destroy,
98         .change_queue_depth             = mptscsih_change_queue_depth,
99         .eh_abort_handler               = mptscsih_abort,
100         .eh_device_reset_handler        = mptscsih_dev_reset,
101         .eh_bus_reset_handler           = mptscsih_bus_reset,
102         .eh_host_reset_handler          = mptscsih_host_reset,
103         .bios_param                     = mptscsih_bios_param,
104         .can_queue                      = MPT_FC_CAN_QUEUE,
105         .this_id                        = -1,
106         .sg_tablesize                   = MPT_SCSI_SG_DEPTH,
107         .max_sectors                    = 8192,
108         .cmd_per_lun                    = 7,
109         .use_clustering                 = ENABLE_CLUSTERING,
110 };
111
112 /****************************************************************************
113  * Supported hardware
114  */
115
116 static struct pci_device_id mptfc_pci_table[] = {
117         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909,
118                 PCI_ANY_ID, PCI_ANY_ID },
119         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919,
120                 PCI_ANY_ID, PCI_ANY_ID },
121         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929,
122                 PCI_ANY_ID, PCI_ANY_ID },
123         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X,
124                 PCI_ANY_ID, PCI_ANY_ID },
125         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X,
126                 PCI_ANY_ID, PCI_ANY_ID },
127         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC939X,
128                 PCI_ANY_ID, PCI_ANY_ID },
129         { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC949X,
130                 PCI_ANY_ID, PCI_ANY_ID },
131         {0}     /* Terminating entry */
132 };
133 MODULE_DEVICE_TABLE(pci, mptfc_pci_table);
134
135 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
136 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
137 /*
138  *      mptfc_probe - Installs scsi devices per bus.
139  *      @pdev: Pointer to pci_dev structure
140  *
141  *      Returns 0 for success, non-zero for failure.
142  *
143  */
144 static int
145 mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
146 {
147         struct Scsi_Host        *sh;
148         MPT_SCSI_HOST           *hd;
149         MPT_ADAPTER             *ioc;
150         unsigned long            flags;
151         int                      sz, ii;
152         int                      numSGE = 0;
153         int                      scale;
154         int                      ioc_cap;
155         u8                      *mem;
156         int                     error=0;
157         int                     r;
158                 
159         if ((r = mpt_attach(pdev,id)) != 0)
160                 return r;
161         
162         ioc = pci_get_drvdata(pdev);
163         ioc->DoneCtx = mptfcDoneCtx;
164         ioc->TaskCtx = mptfcTaskCtx;
165         ioc->InternalCtx = mptfcInternalCtx;
166
167         /*  Added sanity check on readiness of the MPT adapter.
168          */
169         if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
170                 printk(MYIOC_s_WARN_FMT
171                   "Skipping because it's not operational!\n",
172                   ioc->name);
173                 error = -ENODEV;
174                 goto out_mptfc_probe;
175         }
176
177         if (!ioc->active) {
178                 printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
179                   ioc->name);
180                 error = -ENODEV;
181                 goto out_mptfc_probe;
182         }
183
184         /*  Sanity check - ensure at least 1 port is INITIATOR capable
185          */
186         ioc_cap = 0;
187         for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
188                 if (ioc->pfacts[ii].ProtocolFlags &
189                     MPI_PORTFACTS_PROTOCOL_INITIATOR)
190                         ioc_cap ++;
191         }
192
193         if (!ioc_cap) {
194                 printk(MYIOC_s_WARN_FMT
195                         "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n",
196                         ioc->name, ioc);
197                 return 0;
198         }
199
200         sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST));
201
202         if (!sh) {
203                 printk(MYIOC_s_WARN_FMT
204                         "Unable to register controller with SCSI subsystem\n",
205                         ioc->name);
206                 error = -1;
207                 goto out_mptfc_probe;
208         }
209
210         spin_lock_irqsave(&ioc->FreeQlock, flags);
211
212         /* Attach the SCSI Host to the IOC structure
213          */
214         ioc->sh = sh;
215
216         sh->io_port = 0;
217         sh->n_io_port = 0;
218         sh->irq = 0;
219
220         /* set 16 byte cdb's */
221         sh->max_cmd_len = 16;
222
223         sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255;
224
225         sh->max_lun = MPT_LAST_LUN + 1;
226         sh->max_channel = 0;
227         sh->this_id = ioc->pfacts[0].PortSCSIID;
228
229         /* Required entry.
230          */
231         sh->unique_id = ioc->id;
232
233         /* Verify that we won't exceed the maximum
234          * number of chain buffers
235          * We can optimize:  ZZ = req_sz/sizeof(SGE)
236          * For 32bit SGE's:
237          *  numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
238          *               + (req_sz - 64)/sizeof(SGE)
239          * A slightly different algorithm is required for
240          * 64bit SGEs.
241          */
242         scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
243         if (sizeof(dma_addr_t) == sizeof(u64)) {
244                 numSGE = (scale - 1) *
245                   (ioc->facts.MaxChainDepth-1) + scale +
246                   (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
247                   sizeof(u32));
248         } else {
249                 numSGE = 1 + (scale - 1) *
250                   (ioc->facts.MaxChainDepth-1) + scale +
251                   (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
252                   sizeof(u32));
253         }
254
255         if (numSGE < sh->sg_tablesize) {
256                 /* Reset this value */
257                 dprintk((MYIOC_s_INFO_FMT
258                   "Resetting sg_tablesize to %d from %d\n",
259                   ioc->name, numSGE, sh->sg_tablesize));
260                 sh->sg_tablesize = numSGE;
261         }
262
263         spin_unlock_irqrestore(&ioc->FreeQlock, flags);
264
265         hd = (MPT_SCSI_HOST *) sh->hostdata;
266         hd->ioc = ioc;
267
268         /* SCSI needs scsi_cmnd lookup table!
269          * (with size equal to req_depth*PtrSz!)
270          */
271         sz = ioc->req_depth * sizeof(void *);
272         mem = kmalloc(sz, GFP_ATOMIC);
273         if (mem == NULL) {
274                 error = -ENOMEM;
275                 goto out_mptfc_probe;
276         }
277
278         memset(mem, 0, sz);
279         hd->ScsiLookup = (struct scsi_cmnd **) mem;
280
281         dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n",
282                  ioc->name, hd->ScsiLookup, sz));
283
284         /* Allocate memory for the device structures.
285          * A non-Null pointer at an offset
286          * indicates a device exists.
287          * max_id = 1 + maximum id (hosts.h)
288          */
289         sz = sh->max_id * sizeof(void *);
290         mem = kmalloc(sz, GFP_ATOMIC);
291         if (mem == NULL) {
292                 error = -ENOMEM;
293                 goto out_mptfc_probe;
294         }
295
296         memset(mem, 0, sz);
297         hd->Targets = (VirtTarget **) mem;
298
299         dprintk((KERN_INFO
300           "  vdev @ %p, sz=%d\n", hd->Targets, sz));
301
302         /* Clear the TM flags
303          */
304         hd->tmPending = 0;
305         hd->tmState = TM_STATE_NONE;
306         hd->resetPending = 0;
307         hd->abortSCpnt = NULL;
308
309         /* Clear the pointer used to store
310          * single-threaded commands, i.e., those
311          * issued during a bus scan, dv and
312          * configuration pages.
313          */
314         hd->cmdPtr = NULL;
315
316         /* Initialize this SCSI Hosts' timers
317          * To use, set the timer expires field
318          * and add_timer
319          */
320         init_timer(&hd->timer);
321         hd->timer.data = (unsigned long) hd;
322         hd->timer.function = mptscsih_timer_expired;
323
324         hd->mpt_pq_filter = mpt_pq_filter;
325
326         ddvprintk((MYIOC_s_INFO_FMT
327                 "mpt_pq_filter %x\n",
328                 ioc->name, 
329                 mpt_pq_filter));
330
331         init_waitqueue_head(&hd->scandv_waitq);
332         hd->scandv_wait_done = 0;
333         hd->last_queue_full = 0;
334
335         error = scsi_add_host (sh, &ioc->pcidev->dev);
336         if(error) {
337                 dprintk((KERN_ERR MYNAM
338                   "scsi_add_host failed\n"));
339                 goto out_mptfc_probe;
340         }
341
342         scsi_scan_host(sh);
343         return 0;
344
345 out_mptfc_probe:
346
347         mptscsih_remove(pdev);
348         return error;
349 }
350
351 static struct pci_driver mptfc_driver = {
352         .name           = "mptfc",
353         .id_table       = mptfc_pci_table,
354         .probe          = mptfc_probe,
355         .remove         = __devexit_p(mptscsih_remove),
356         .shutdown       = mptscsih_shutdown,
357 #ifdef CONFIG_PM
358         .suspend        = mptscsih_suspend,
359         .resume         = mptscsih_resume,
360 #endif
361 };
362
363 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
364 /**
365  *      mptfc_init - Register MPT adapter(s) as SCSI host(s) with
366  *      linux scsi mid-layer.
367  *
368  *      Returns 0 for success, non-zero for failure.
369  */
370 static int __init
371 mptfc_init(void)
372 {
373
374         show_mptmod_ver(my_NAME, my_VERSION);
375
376         mptfcDoneCtx = mpt_register(mptscsih_io_done, MPTFC_DRIVER);
377         mptfcTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTFC_DRIVER);
378         mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER);
379
380         if (mpt_event_register(mptfcDoneCtx, mptscsih_event_process) == 0) {
381                 devtprintk((KERN_INFO MYNAM
382                   ": Registered for IOC event notifications\n"));
383         }
384
385         if (mpt_reset_register(mptfcDoneCtx, mptscsih_ioc_reset) == 0) {
386                 dprintk((KERN_INFO MYNAM
387                   ": Registered for IOC reset notifications\n"));
388         }
389
390         return pci_register_driver(&mptfc_driver);
391 }
392
393 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
394 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
395 /**
396  *      mptfc_exit - Unregisters MPT adapter(s)
397  *
398  */
399 static void __exit
400 mptfc_exit(void)
401 {
402         pci_unregister_driver(&mptfc_driver);
403         
404         mpt_reset_deregister(mptfcDoneCtx);
405         dprintk((KERN_INFO MYNAM
406           ": Deregistered for IOC reset notifications\n"));
407
408         mpt_event_deregister(mptfcDoneCtx);
409         dprintk((KERN_INFO MYNAM
410           ": Deregistered for IOC event notifications\n"));
411
412         mpt_deregister(mptfcInternalCtx);
413         mpt_deregister(mptfcTaskCtx);
414         mpt_deregister(mptfcDoneCtx);
415 }
416
417 module_init(mptfc_init);
418 module_exit(mptfc_exit);