aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/message/fusion/mptfc.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
new file mode 100644
index 000000000000..91c79e525d3c
--- /dev/null
+++ b/drivers/message/fusion/mptfc.c
@@ -0,0 +1,428 @@
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
73MODULE_AUTHOR(MODULEAUTHOR);
74MODULE_DESCRIPTION(my_NAME);
75MODULE_LICENSE("GPL");
76
77/* Command line args */
78static int mpt_pq_filter = 0;
79module_param(mpt_pq_filter, int, 0);
80MODULE_PARM_DESC(mpt_pq_filter, " Enable peripheral qualifier filter: enable=1 (default=0)");
81
82static int mptfcDoneCtx = -1;
83static int mptfcTaskCtx = -1;
84static int mptfcInternalCtx = -1; /* Used only for internal commands */
85
86static struct device_attribute mptfc_queue_depth_attr = {
87 .attr = {
88 .name = "queue_depth",
89 .mode = S_IWUSR,
90 },
91 .store = mptscsih_store_queue_depth,
92};
93
94static struct device_attribute *mptfc_dev_attrs[] = {
95 &mptfc_queue_depth_attr,
96 NULL,
97};
98
99static struct scsi_host_template mptfc_driver_template = {
100 .proc_name = "mptfc",
101 .proc_info = mptscsih_proc_info,
102 .name = "MPT FC Host",
103 .info = mptscsih_info,
104 .queuecommand = mptscsih_qcmd,
105 .slave_alloc = mptscsih_slave_alloc,
106 .slave_configure = mptscsih_slave_configure,
107 .slave_destroy = mptscsih_slave_destroy,
108 .eh_abort_handler = mptscsih_abort,
109 .eh_device_reset_handler = mptscsih_dev_reset,
110 .eh_bus_reset_handler = mptscsih_bus_reset,
111 .eh_host_reset_handler = mptscsih_host_reset,
112 .bios_param = mptscsih_bios_param,
113 .can_queue = MPT_FC_CAN_QUEUE,
114 .this_id = -1,
115 .sg_tablesize = MPT_SCSI_SG_DEPTH,
116 .max_sectors = 8192,
117 .cmd_per_lun = 7,
118 .use_clustering = ENABLE_CLUSTERING,
119 .sdev_attrs = mptfc_dev_attrs,
120};
121
122/****************************************************************************
123 * Supported hardware
124 */
125
126static struct pci_device_id mptfc_pci_table[] = {
127 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909,
128 PCI_ANY_ID, PCI_ANY_ID },
129 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919,
130 PCI_ANY_ID, PCI_ANY_ID },
131 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929,
132 PCI_ANY_ID, PCI_ANY_ID },
133 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X,
134 PCI_ANY_ID, PCI_ANY_ID },
135 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X,
136 PCI_ANY_ID, PCI_ANY_ID },
137 {0} /* Terminating entry */
138};
139MODULE_DEVICE_TABLE(pci, mptfc_pci_table);
140
141/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
142/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
143/*
144 * mptfc_probe - Installs scsi devices per bus.
145 * @pdev: Pointer to pci_dev structure
146 *
147 * Returns 0 for success, non-zero for failure.
148 *
149 */
150static int
151mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
152{
153 struct Scsi_Host *sh;
154 MPT_SCSI_HOST *hd;
155 MPT_ADAPTER *ioc;
156 unsigned long flags;
157 int sz, ii;
158 int numSGE = 0;
159 int scale;
160 int ioc_cap;
161 u8 *mem;
162 int error=0;
163 int r;
164
165 if ((r = mpt_attach(pdev,id)) != 0)
166 return r;
167
168 ioc = pci_get_drvdata(pdev);
169
170 /* Added sanity check on readiness of the MPT adapter.
171 */
172 if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
173 printk(MYIOC_s_WARN_FMT
174 "Skipping because it's not operational!\n",
175 ioc->name);
176 return -ENODEV;
177 }
178
179 if (!ioc->active) {
180 printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
181 ioc->name);
182 return -ENODEV;
183 }
184
185 /* Sanity check - ensure at least 1 port is INITIATOR capable
186 */
187 ioc_cap = 0;
188 for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
189 if (ioc->pfacts[ii].ProtocolFlags &
190 MPI_PORTFACTS_PROTOCOL_INITIATOR)
191 ioc_cap ++;
192 }
193
194 if (!ioc_cap) {
195 printk(MYIOC_s_WARN_FMT
196 "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n",
197 ioc->name, ioc);
198 return -ENODEV;
199 }
200
201 sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST));
202
203 if (!sh) {
204 printk(MYIOC_s_WARN_FMT
205 "Unable to register controller with SCSI subsystem\n",
206 ioc->name);
207 return -1;
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 /* Set the pci device pointer in Scsi_Host structure.
264 */
265 scsi_set_device(sh, &ioc->pcidev->dev);
266
267 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
268
269 hd = (MPT_SCSI_HOST *) sh->hostdata;
270 hd->ioc = ioc;
271
272 /* SCSI needs scsi_cmnd lookup table!
273 * (with size equal to req_depth*PtrSz!)
274 */
275 sz = ioc->req_depth * sizeof(void *);
276 mem = kmalloc(sz, GFP_ATOMIC);
277 if (mem == NULL) {
278 error = -ENOMEM;
279 goto mptfc_probe_failed;
280 }
281
282 memset(mem, 0, sz);
283 hd->ScsiLookup = (struct scsi_cmnd **) mem;
284
285 dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n",
286 ioc->name, hd->ScsiLookup, sz));
287
288 /* Allocate memory for the device structures.
289 * A non-Null pointer at an offset
290 * indicates a device exists.
291 * max_id = 1 + maximum id (hosts.h)
292 */
293 sz = sh->max_id * sizeof(void *);
294 mem = kmalloc(sz, GFP_ATOMIC);
295 if (mem == NULL) {
296 error = -ENOMEM;
297 goto mptfc_probe_failed;
298 }
299
300 memset(mem, 0, sz);
301 hd->Targets = (VirtDevice **) mem;
302
303 dprintk((KERN_INFO
304 " Targets @ %p, sz=%d\n", hd->Targets, sz));
305
306 /* Clear the TM flags
307 */
308 hd->tmPending = 0;
309 hd->tmState = TM_STATE_NONE;
310 hd->resetPending = 0;
311 hd->abortSCpnt = NULL;
312
313 /* Clear the pointer used to store
314 * single-threaded commands, i.e., those
315 * issued during a bus scan, dv and
316 * configuration pages.
317 */
318 hd->cmdPtr = NULL;
319
320 /* Initialize this SCSI Hosts' timers
321 * To use, set the timer expires field
322 * and add_timer
323 */
324 init_timer(&hd->timer);
325 hd->timer.data = (unsigned long) hd;
326 hd->timer.function = mptscsih_timer_expired;
327
328 ioc->DoneCtx = mptfcDoneCtx;
329 ioc->TaskCtx = mptfcTaskCtx;
330 ioc->InternalCtx = mptfcInternalCtx;
331
332 hd->mpt_pq_filter = mpt_pq_filter;
333
334 ddvprintk((MYIOC_s_INFO_FMT
335 "mpt_pq_filter %x\n",
336 ioc->name,
337 mpt_pq_filter));
338
339 init_waitqueue_head(&hd->scandv_waitq);
340 hd->scandv_wait_done = 0;
341 hd->last_queue_full = 0;
342
343 error = scsi_add_host (sh, &ioc->pcidev->dev);
344 if(error) {
345 dprintk((KERN_ERR MYNAM
346 "scsi_add_host failed\n"));
347 goto mptfc_probe_failed;
348 }
349
350 scsi_scan_host(sh);
351 return 0;
352
353mptfc_probe_failed:
354
355 mptscsih_remove(pdev);
356 return error;
357}
358
359static struct pci_driver mptfc_driver = {
360 .name = "mptfc",
361 .id_table = mptfc_pci_table,
362 .probe = mptfc_probe,
363 .remove = __devexit_p(mptscsih_remove),
364 .driver = {
365 .shutdown = mptscsih_shutdown,
366 },
367#ifdef CONFIG_PM
368 .suspend = mptscsih_suspend,
369 .resume = mptscsih_resume,
370#endif
371};
372
373/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
374/**
375 * mptfc_init - Register MPT adapter(s) as SCSI host(s) with
376 * linux scsi mid-layer.
377 *
378 * Returns 0 for success, non-zero for failure.
379 */
380static int __init
381mptfc_init(void)
382{
383
384 show_mptmod_ver(my_NAME, my_VERSION);
385
386 mptfcDoneCtx = mpt_register(mptscsih_io_done, MPTFC_DRIVER);
387 mptfcTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTFC_DRIVER);
388 mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER);
389
390 if (mpt_event_register(mptfcDoneCtx, mptscsih_event_process) == 0) {
391 devtprintk((KERN_INFO MYNAM
392 ": Registered for IOC event notifications\n"));
393 }
394
395 if (mpt_reset_register(mptfcDoneCtx, mptscsih_ioc_reset) == 0) {
396 dprintk((KERN_INFO MYNAM
397 ": Registered for IOC reset notifications\n"));
398 }
399
400 return pci_register_driver(&mptfc_driver);
401}
402
403/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
404/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
405/**
406 * mptfc_exit - Unregisters MPT adapter(s)
407 *
408 */
409static void __exit
410mptfc_exit(void)
411{
412 pci_unregister_driver(&mptfc_driver);
413
414 mpt_reset_deregister(mptfcDoneCtx);
415 dprintk((KERN_INFO MYNAM
416 ": Deregistered for IOC reset notifications\n"));
417
418 mpt_event_deregister(mptfcDoneCtx);
419 dprintk((KERN_INFO MYNAM
420 ": Deregistered for IOC event notifications\n"));
421
422 mpt_deregister(mptfcInternalCtx);
423 mpt_deregister(mptfcTaskCtx);
424 mpt_deregister(mptfcDoneCtx);
425}
426
427module_init(mptfc_init);
428module_exit(mptfc_exit);