blob: e518bc97f8ce5a2c72289b32be4cd8123ef05fa9 [file] [log] [blame]
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001/*
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>
Michael Reed05e8ec12006-01-13 14:31:54 -060058#include <linux/sort.h>
Moore, Eric Dean 2496af32005-04-22 18:02:41 -040059
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>
Michael Reed05e8ec12006-01-13 14:31:54 -060065#include <scsi/scsi_transport_fc.h>
Moore, Eric Dean 2496af32005-04-22 18:02:41 -040066
67#include "mptbase.h"
68#include "mptscsih.h"
69
70/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
71#define my_NAME "Fusion MPT FC Host driver"
72#define my_VERSION MPT_LINUX_VERSION_COMMON
73#define MYNAM "mptfc"
74
75MODULE_AUTHOR(MODULEAUTHOR);
76MODULE_DESCRIPTION(my_NAME);
77MODULE_LICENSE("GPL");
78
79/* Command line args */
80static int mpt_pq_filter = 0;
81module_param(mpt_pq_filter, int, 0);
82MODULE_PARM_DESC(mpt_pq_filter, " Enable peripheral qualifier filter: enable=1 (default=0)");
83
Michael Reed05e8ec12006-01-13 14:31:54 -060084#define MPTFC_DEV_LOSS_TMO (60)
85static int mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; /* reasonable default */
86module_param(mptfc_dev_loss_tmo, int, 0);
87MODULE_PARM_DESC(mptfc_dev_loss_tmo, " Initial time the driver programs the "
88 " transport to wait for an rport to "
89 " return following a device loss event."
90 " Default=60.");
91
Moore, Eric Dean 2496af32005-04-22 18:02:41 -040092static int mptfcDoneCtx = -1;
93static int mptfcTaskCtx = -1;
94static int mptfcInternalCtx = -1; /* Used only for internal commands */
95
Michael Reed3bc7bf12006-01-25 18:05:18 -070096static int mptfc_target_alloc(struct scsi_target *starget);
97static int mptfc_slave_alloc(struct scsi_device *sdev);
Michael Reed05e8ec12006-01-13 14:31:54 -060098static int mptfc_qcmd(struct scsi_cmnd *SCpnt,
Michael Reed3bc7bf12006-01-25 18:05:18 -070099 void (*done)(struct scsi_cmnd *));
100static void mptfc_target_destroy(struct scsi_target *starget);
Michael Reed05e8ec12006-01-13 14:31:54 -0600101static void mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout);
102static void __devexit mptfc_remove(struct pci_dev *pdev);
103
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400104static struct scsi_host_template mptfc_driver_template = {
Moore, Eric Deanf78496d2005-11-16 18:54:14 -0700105 .module = THIS_MODULE,
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400106 .proc_name = "mptfc",
107 .proc_info = mptscsih_proc_info,
108 .name = "MPT FC Host",
109 .info = mptscsih_info,
Michael Reed05e8ec12006-01-13 14:31:54 -0600110 .queuecommand = mptfc_qcmd,
Michael Reed3bc7bf12006-01-25 18:05:18 -0700111 .target_alloc = mptfc_target_alloc,
Michael Reed05e8ec12006-01-13 14:31:54 -0600112 .slave_alloc = mptfc_slave_alloc,
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400113 .slave_configure = mptscsih_slave_configure,
Michael Reed3bc7bf12006-01-25 18:05:18 -0700114 .target_destroy = mptfc_target_destroy,
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400115 .slave_destroy = mptscsih_slave_destroy,
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -0600116 .change_queue_depth = mptscsih_change_queue_depth,
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400117 .eh_abort_handler = mptscsih_abort,
118 .eh_device_reset_handler = mptscsih_dev_reset,
119 .eh_bus_reset_handler = mptscsih_bus_reset,
120 .eh_host_reset_handler = mptscsih_host_reset,
121 .bios_param = mptscsih_bios_param,
122 .can_queue = MPT_FC_CAN_QUEUE,
123 .this_id = -1,
124 .sg_tablesize = MPT_SCSI_SG_DEPTH,
125 .max_sectors = 8192,
126 .cmd_per_lun = 7,
127 .use_clustering = ENABLE_CLUSTERING,
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400128};
129
130/****************************************************************************
131 * Supported hardware
132 */
133
134static struct pci_device_id mptfc_pci_table[] = {
135 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909,
136 PCI_ANY_ID, PCI_ANY_ID },
137 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919,
138 PCI_ANY_ID, PCI_ANY_ID },
139 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929,
140 PCI_ANY_ID, PCI_ANY_ID },
141 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X,
142 PCI_ANY_ID, PCI_ANY_ID },
143 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X,
144 PCI_ANY_ID, PCI_ANY_ID },
Moore, Eric Dean 3fadc592005-05-11 17:46:52 -0600145 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC939X,
146 PCI_ANY_ID, PCI_ANY_ID },
147 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC949X,
148 PCI_ANY_ID, PCI_ANY_ID },
Moore, Eric6d5b0c32006-01-13 16:25:26 -0700149 { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC949ES,
150 PCI_ANY_ID, PCI_ANY_ID },
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400151 {0} /* Terminating entry */
152};
153MODULE_DEVICE_TABLE(pci, mptfc_pci_table);
154
Michael Reed05e8ec12006-01-13 14:31:54 -0600155static struct scsi_transport_template *mptfc_transport_template = NULL;
156
Adrian Bunk03fbcbc2006-01-25 02:00:52 +0100157static struct fc_function_template mptfc_transport_functions = {
Michael Reed05e8ec12006-01-13 14:31:54 -0600158 .dd_fcrport_size = 8,
159 .show_host_node_name = 1,
160 .show_host_port_name = 1,
161 .show_host_supported_classes = 1,
162 .show_host_port_id = 1,
163 .show_rport_supported_classes = 1,
164 .show_starget_node_name = 1,
165 .show_starget_port_name = 1,
166 .show_starget_port_id = 1,
167 .set_rport_dev_loss_tmo = mptfc_set_rport_loss_tmo,
168 .show_rport_dev_loss_tmo = 1,
169
170};
171
172/* FIXME! values controlling firmware RESCAN event
173 * need to be set low to allow dev_loss_tmo to
174 * work as expected. Currently, firmware doesn't
175 * notify driver of RESCAN event until some number
176 * of seconds elapse. This value can be set via
177 * lsiutil.
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400178 */
Michael Reed05e8ec12006-01-13 14:31:54 -0600179static void
180mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
181{
182 if (timeout > 0)
183 rport->dev_loss_tmo = timeout;
184 else
185 rport->dev_loss_tmo = mptfc_dev_loss_tmo;
186}
187
188static int
189mptfc_FcDevPage0_cmp_func(const void *a, const void *b)
190{
191 FCDevicePage0_t **aa = (FCDevicePage0_t **)a;
192 FCDevicePage0_t **bb = (FCDevicePage0_t **)b;
193
194 if ((*aa)->CurrentBus == (*bb)->CurrentBus) {
195 if ((*aa)->CurrentTargetID == (*bb)->CurrentTargetID)
196 return 0;
197 if ((*aa)->CurrentTargetID < (*bb)->CurrentTargetID)
198 return -1;
199 return 1;
200 }
201 if ((*aa)->CurrentBus < (*bb)->CurrentBus)
202 return -1;
203 return 1;
204}
205
206static int
207mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, int ioc_port,
208 void(*func)(MPT_ADAPTER *ioc,int channel, FCDevicePage0_t *arg))
209{
210 ConfigPageHeader_t hdr;
211 CONFIGPARMS cfg;
212 FCDevicePage0_t *ppage0_alloc, *fc;
213 dma_addr_t page0_dma;
214 int data_sz;
215 int ii;
216
217 FCDevicePage0_t *p0_array=NULL, *p_p0;
218 FCDevicePage0_t **pp0_array=NULL, **p_pp0;
219
220 int rc = -ENOMEM;
221 U32 port_id = 0xffffff;
222 int num_targ = 0;
223 int max_bus = ioc->facts.MaxBuses;
224 int max_targ = ioc->facts.MaxDevices;
225
226 if (max_bus == 0 || max_targ == 0)
227 goto out;
228
229 data_sz = sizeof(FCDevicePage0_t) * max_bus * max_targ;
230 p_p0 = p0_array = kzalloc(data_sz, GFP_KERNEL);
231 if (!p0_array)
232 goto out;
233
234 data_sz = sizeof(FCDevicePage0_t *) * max_bus * max_targ;
235 p_pp0 = pp0_array = kzalloc(data_sz, GFP_KERNEL);
236 if (!pp0_array)
237 goto out;
238
239 do {
240 /* Get FC Device Page 0 header */
241 hdr.PageVersion = 0;
242 hdr.PageLength = 0;
243 hdr.PageNumber = 0;
244 hdr.PageType = MPI_CONFIG_PAGETYPE_FC_DEVICE;
245 cfg.cfghdr.hdr = &hdr;
246 cfg.physAddr = -1;
247 cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
248 cfg.dir = 0;
249 cfg.pageAddr = port_id;
250 cfg.timeout = 0;
251
252 if ((rc = mpt_config(ioc, &cfg)) != 0)
253 break;
254
255 if (hdr.PageLength <= 0)
256 break;
257
258 data_sz = hdr.PageLength * 4;
259 ppage0_alloc = pci_alloc_consistent(ioc->pcidev, data_sz,
260 &page0_dma);
261 rc = -ENOMEM;
262 if (!ppage0_alloc)
263 break;
264
265 cfg.physAddr = page0_dma;
266 cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
267
268 if ((rc = mpt_config(ioc, &cfg)) == 0) {
269 ppage0_alloc->PortIdentifier =
270 le32_to_cpu(ppage0_alloc->PortIdentifier);
271
272 ppage0_alloc->WWNN.Low =
273 le32_to_cpu(ppage0_alloc->WWNN.Low);
274
275 ppage0_alloc->WWNN.High =
276 le32_to_cpu(ppage0_alloc->WWNN.High);
277
278 ppage0_alloc->WWPN.Low =
279 le32_to_cpu(ppage0_alloc->WWPN.Low);
280
281 ppage0_alloc->WWPN.High =
282 le32_to_cpu(ppage0_alloc->WWPN.High);
283
284 ppage0_alloc->BBCredit =
285 le16_to_cpu(ppage0_alloc->BBCredit);
286
287 ppage0_alloc->MaxRxFrameSize =
288 le16_to_cpu(ppage0_alloc->MaxRxFrameSize);
289
290 port_id = ppage0_alloc->PortIdentifier;
291 num_targ++;
292 *p_p0 = *ppage0_alloc; /* save data */
293 *p_pp0++ = p_p0++; /* save addr */
294 }
295 pci_free_consistent(ioc->pcidev, data_sz,
296 (u8 *) ppage0_alloc, page0_dma);
297 if (rc != 0)
298 break;
299
300 } while (port_id <= 0xff0000);
301
302 if (num_targ) {
303 /* sort array */
304 if (num_targ > 1)
305 sort (pp0_array, num_targ, sizeof(FCDevicePage0_t *),
306 mptfc_FcDevPage0_cmp_func, NULL);
307 /* call caller's func for each targ */
308 for (ii = 0; ii < num_targ; ii++) {
309 fc = *(pp0_array+ii);
310 func(ioc, ioc_port, fc);
311 }
312 }
313
314 out:
315 if (pp0_array)
316 kfree(pp0_array);
317 if (p0_array)
318 kfree(p0_array);
319 return rc;
320}
321
322static int
323mptfc_generate_rport_ids(FCDevicePage0_t *pg0, struct fc_rport_identifiers *rid)
324{
325 /* not currently usable */
326 if (pg0->Flags & (MPI_FC_DEVICE_PAGE0_FLAGS_PLOGI_INVALID |
327 MPI_FC_DEVICE_PAGE0_FLAGS_PRLI_INVALID))
328 return -1;
329
330 if (!(pg0->Flags & MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID))
331 return -1;
332
333 if (!(pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET))
334 return -1;
335
336 /*
337 * board data structure already normalized to platform endianness
338 * shifted to avoid unaligned access on 64 bit architecture
339 */
340 rid->node_name = ((u64)pg0->WWNN.High) << 32 | (u64)pg0->WWNN.Low;
341 rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low;
342 rid->port_id = pg0->PortIdentifier;
343 rid->roles = FC_RPORT_ROLE_UNKNOWN;
Michael Reed05e8ec12006-01-13 14:31:54 -0600344
345 return 0;
346}
347
348static void
349mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0)
350{
351 struct fc_rport_identifiers rport_ids;
352 struct fc_rport *rport;
353 struct mptfc_rport_info *ri;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700354 int new_ri = 1;
Moore, Eric65207fe2006-04-21 16:14:35 -0600355 u64 pn, nn;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700356 VirtTarget *vtarget;
mdr@sgi.com6dd727d2006-05-01 13:07:04 -0500357 u32 roles = FC_RPORT_ROLE_UNKNOWN;
Michael Reed05e8ec12006-01-13 14:31:54 -0600358
359 if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0)
360 return;
361
mdr@sgi.com6dd727d2006-05-01 13:07:04 -0500362 roles |= FC_RPORT_ROLE_FCP_TARGET;
363 if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR)
364 roles |= FC_RPORT_ROLE_FCP_INITIATOR;
365
Michael Reed05e8ec12006-01-13 14:31:54 -0600366 /* scan list looking for a match */
Michael Reed05e8ec12006-01-13 14:31:54 -0600367 list_for_each_entry(ri, &ioc->fc_rports, list) {
Michael Reed3bc7bf12006-01-25 18:05:18 -0700368 pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low;
369 if (pn == rport_ids.port_name) { /* match */
Michael Reed05e8ec12006-01-13 14:31:54 -0600370 list_move_tail(&ri->list, &ioc->fc_rports);
Michael Reed3bc7bf12006-01-25 18:05:18 -0700371 new_ri = 0;
Michael Reed05e8ec12006-01-13 14:31:54 -0600372 break;
373 }
374 }
Michael Reed3bc7bf12006-01-25 18:05:18 -0700375 if (new_ri) { /* allocate one */
Michael Reed05e8ec12006-01-13 14:31:54 -0600376 ri = kzalloc(sizeof(struct mptfc_rport_info), GFP_KERNEL);
377 if (!ri)
378 return;
Michael Reed05e8ec12006-01-13 14:31:54 -0600379 list_add_tail(&ri->list, &ioc->fc_rports);
380 }
381
382 ri->pg0 = *pg0; /* add/update pg0 data */
383 ri->flags &= ~MPT_RPORT_INFO_FLAGS_MISSING;
384
Michael Reed3bc7bf12006-01-25 18:05:18 -0700385 /* MPT_RPORT_INFO_FLAGS_REGISTERED - rport not previously deleted */
Michael Reed05e8ec12006-01-13 14:31:54 -0600386 if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) {
387 ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700388 rport = fc_remote_port_add(ioc->sh, channel, &rport_ids);
Michael Reed05e8ec12006-01-13 14:31:54 -0600389 if (rport) {
Michael Reed3bc7bf12006-01-25 18:05:18 -0700390 ri->rport = rport;
391 if (new_ri) /* may have been reset by user */
392 rport->dev_loss_tmo = mptfc_dev_loss_tmo;
Michael Reed05e8ec12006-01-13 14:31:54 -0600393 /*
394 * if already mapped, remap here. If not mapped,
Michael Reed3bc7bf12006-01-25 18:05:18 -0700395 * target_alloc will allocate vtarget and map,
396 * slave_alloc will fill in vdev from vtarget.
Michael Reed05e8ec12006-01-13 14:31:54 -0600397 */
Michael Reed3bc7bf12006-01-25 18:05:18 -0700398 if (ri->starget) {
399 vtarget = ri->starget->hostdata;
400 if (vtarget) {
401 vtarget->target_id = pg0->CurrentTargetID;
402 vtarget->bus_id = pg0->CurrentBus;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700403 }
Michael Reed05e8ec12006-01-13 14:31:54 -0600404 }
Moore, Eric65207fe2006-04-21 16:14:35 -0600405 *((struct mptfc_rport_info **)rport->dd_data) = ri;
mdr@sgi.com6dd727d2006-05-01 13:07:04 -0500406 /* scan will be scheduled once rport becomes a target */
407 fc_remote_port_rolechg(rport,roles);
Moore, Eric65207fe2006-04-21 16:14:35 -0600408
409 pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low;
410 nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700411 dfcprintk ((MYIOC_s_INFO_FMT
412 "mptfc_reg_dev.%d: %x, %llx / %llx, tid %d, "
Michael Reed05e8ec12006-01-13 14:31:54 -0600413 "rport tid %d, tmo %d\n",
Michael Reed3bc7bf12006-01-25 18:05:18 -0700414 ioc->name,
Moore, Eric914c2d82006-03-14 09:19:36 -0700415 ioc->sh->host_no,
Michael Reed05e8ec12006-01-13 14:31:54 -0600416 pg0->PortIdentifier,
Moore, Eric65207fe2006-04-21 16:14:35 -0600417 (unsigned long long)nn,
418 (unsigned long long)pn,
Michael Reed05e8ec12006-01-13 14:31:54 -0600419 pg0->CurrentTargetID,
420 ri->rport->scsi_target_id,
Michael Reed3bc7bf12006-01-25 18:05:18 -0700421 ri->rport->dev_loss_tmo));
Michael Reed05e8ec12006-01-13 14:31:54 -0600422 } else {
423 list_del(&ri->list);
424 kfree(ri);
425 ri = NULL;
426 }
427 }
Michael Reed05e8ec12006-01-13 14:31:54 -0600428}
429
430/*
Michael Reed3bc7bf12006-01-25 18:05:18 -0700431 * OS entry point to allow for host driver to free allocated memory
432 * Called if no device present or device being unloaded
433 */
434static void
435mptfc_target_destroy(struct scsi_target *starget)
436{
437 struct fc_rport *rport;
438 struct mptfc_rport_info *ri;
439
440 rport = starget_to_rport(starget);
441 if (rport) {
442 ri = *((struct mptfc_rport_info **)rport->dd_data);
443 if (ri) /* better be! */
444 ri->starget = NULL;
445 }
446 if (starget->hostdata)
447 kfree(starget->hostdata);
448 starget->hostdata = NULL;
449}
450
451/*
452 * OS entry point to allow host driver to alloc memory
453 * for each scsi target. Called once per device the bus scan.
454 * Return non-zero if allocation fails.
455 */
456static int
457mptfc_target_alloc(struct scsi_target *starget)
458{
459 VirtTarget *vtarget;
460 struct fc_rport *rport;
461 struct mptfc_rport_info *ri;
462 int rc;
463
464 vtarget = kzalloc(sizeof(VirtTarget), GFP_KERNEL);
465 if (!vtarget)
466 return -ENOMEM;
467 starget->hostdata = vtarget;
468
469 rc = -ENODEV;
470 rport = starget_to_rport(starget);
471 if (rport) {
472 ri = *((struct mptfc_rport_info **)rport->dd_data);
473 if (ri) { /* better be! */
474 vtarget->target_id = ri->pg0.CurrentTargetID;
475 vtarget->bus_id = ri->pg0.CurrentBus;
476 ri->starget = starget;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700477 rc = 0;
478 }
479 }
480 if (rc != 0) {
481 kfree(vtarget);
482 starget->hostdata = NULL;
483 }
484
485 return rc;
486}
487
488/*
Michael Reed05e8ec12006-01-13 14:31:54 -0600489 * OS entry point to allow host driver to alloc memory
490 * for each scsi device. Called once per device the bus scan.
491 * Return non-zero if allocation fails.
492 * Init memory once per LUN.
493 */
Adrian Bunk03fbcbc2006-01-25 02:00:52 +0100494static int
Michael Reed05e8ec12006-01-13 14:31:54 -0600495mptfc_slave_alloc(struct scsi_device *sdev)
496{
497 MPT_SCSI_HOST *hd;
498 VirtTarget *vtarget;
499 VirtDevice *vdev;
500 struct scsi_target *starget;
501 struct fc_rport *rport;
Michael Reed05e8ec12006-01-13 14:31:54 -0600502
503
Moore, Eric65207fe2006-04-21 16:14:35 -0600504 starget = scsi_target(sdev);
505 rport = starget_to_rport(starget);
Michael Reed05e8ec12006-01-13 14:31:54 -0600506
507 if (!rport || fc_remote_port_chkready(rport))
508 return -ENXIO;
509
510 hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
511
Michael Reed3bc7bf12006-01-25 18:05:18 -0700512 vdev = kzalloc(sizeof(VirtDevice), GFP_KERNEL);
Michael Reed05e8ec12006-01-13 14:31:54 -0600513 if (!vdev) {
514 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
515 hd->ioc->name, sizeof(VirtDevice));
516 return -ENOMEM;
517 }
Michael Reed05e8ec12006-01-13 14:31:54 -0600518
Michael Reed05e8ec12006-01-13 14:31:54 -0600519
Michael Reed05e8ec12006-01-13 14:31:54 -0600520 sdev->hostdata = vdev;
Michael Reed05e8ec12006-01-13 14:31:54 -0600521 vtarget = starget->hostdata;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700522
Michael Reed05e8ec12006-01-13 14:31:54 -0600523 if (vtarget->num_luns == 0) {
Michael Reed3bc7bf12006-01-25 18:05:18 -0700524 vtarget->ioc_id = hd->ioc->id;
Michael Reed05e8ec12006-01-13 14:31:54 -0600525 vtarget->tflags = MPT_TARGET_FLAGS_Q_YES |
526 MPT_TARGET_FLAGS_VALID_INQUIRY;
527 hd->Targets[sdev->id] = vtarget;
528 }
529
Michael Reed05e8ec12006-01-13 14:31:54 -0600530 vdev->vtarget = vtarget;
Michael Reed05e8ec12006-01-13 14:31:54 -0600531 vdev->lun = sdev->lun;
Michael Reed05e8ec12006-01-13 14:31:54 -0600532
Michael Reed05e8ec12006-01-13 14:31:54 -0600533 vtarget->num_luns++;
534
Moore, Eric65207fe2006-04-21 16:14:35 -0600535
Moore, Eric914c2d82006-03-14 09:19:36 -0700536#ifdef DMPT_DEBUG_FC
Moore, Eric65207fe2006-04-21 16:14:35 -0600537 {
538 u64 nn, pn;
Moore, Eric914c2d82006-03-14 09:19:36 -0700539 struct mptfc_rport_info *ri;
540 ri = *((struct mptfc_rport_info **)rport->dd_data);
Moore, Eric65207fe2006-04-21 16:14:35 -0600541 pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low;
542 nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700543 dfcprintk ((MYIOC_s_INFO_FMT
544 "mptfc_slv_alloc.%d: num_luns %d, sdev.id %d, "
Michael Reed05e8ec12006-01-13 14:31:54 -0600545 "CurrentTargetID %d, %x %llx %llx\n",
Moore, Eric914c2d82006-03-14 09:19:36 -0700546 hd->ioc->name,
Michael Reed3bc7bf12006-01-25 18:05:18 -0700547 sdev->host->host_no,
548 vtarget->num_luns,
549 sdev->id, ri->pg0.CurrentTargetID,
Moore, Eric65207fe2006-04-21 16:14:35 -0600550 ri->pg0.PortIdentifier,
551 (unsigned long long)pn,
552 (unsigned long long)nn));
Moore, Eric914c2d82006-03-14 09:19:36 -0700553 }
554#endif
Michael Reed05e8ec12006-01-13 14:31:54 -0600555
556 return 0;
557}
558
559static int
560mptfc_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
561{
Michael Reed3bc7bf12006-01-25 18:05:18 -0700562 struct mptfc_rport_info *ri;
Michael Reed05e8ec12006-01-13 14:31:54 -0600563 struct fc_rport *rport = starget_to_rport(scsi_target(SCpnt->device));
564 int err;
565
566 err = fc_remote_port_chkready(rport);
567 if (unlikely(err)) {
568 SCpnt->result = err;
569 done(SCpnt);
570 return 0;
571 }
Michael Reed3bc7bf12006-01-25 18:05:18 -0700572
Moore, Eric65207fe2006-04-21 16:14:35 -0600573 /* dd_data is null until finished adding target */
574 ri = *((struct mptfc_rport_info **)rport->dd_data);
575 if (unlikely(!ri)) {
576 dfcprintk ((MYIOC_s_INFO_FMT
577 "mptfc_qcmd.%d: %d:%d, dd_data is null.\n",
578 ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->name,
579 ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->sh->host_no,
580 SCpnt->device->id,SCpnt->device->lun));
581 SCpnt->result = DID_IMM_RETRY << 16;
582 done(SCpnt);
583 return 0;
584 }
585
586 err = mptscsih_qcmd(SCpnt,done);
587#ifdef DMPT_DEBUG_FC
588 if (unlikely(err)) {
589 dfcprintk ((MYIOC_s_INFO_FMT
590 "mptfc_qcmd.%d: %d:%d, mptscsih_qcmd returns non-zero.\n",
591 ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->name,
592 ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->sh->host_no,
593 SCpnt->device->id,SCpnt->device->lun));
594 }
595#endif
596 return err;
Michael Reed05e8ec12006-01-13 14:31:54 -0600597}
598
Michael Reed80d3ac72006-05-24 15:07:09 -0500599/*
600 * mptfc_GetFcPortPage0 - Fetch FCPort config Page0.
601 * @ioc: Pointer to MPT_ADAPTER structure
602 * @portnum: IOC Port number
603 *
604 * Return: 0 for success
605 * -ENOMEM if no memory available
606 * -EPERM if not allowed due to ISR context
607 * -EAGAIN if no msg frames currently available
608 * -EFAULT for non-successful reply or no reply (timeout)
609 * -EINVAL portnum arg out of range (hardwired to two elements)
610 */
611static int
612mptfc_GetFcPortPage0(MPT_ADAPTER *ioc, int portnum)
613{
614 ConfigPageHeader_t hdr;
615 CONFIGPARMS cfg;
616 FCPortPage0_t *ppage0_alloc;
617 FCPortPage0_t *pp0dest;
618 dma_addr_t page0_dma;
619 int data_sz;
620 int copy_sz;
621 int rc;
622 int count = 400;
623
624 if (portnum > 1)
625 return -EINVAL;
626
627 /* Get FCPort Page 0 header */
628 hdr.PageVersion = 0;
629 hdr.PageLength = 0;
630 hdr.PageNumber = 0;
631 hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT;
632 cfg.cfghdr.hdr = &hdr;
633 cfg.physAddr = -1;
634 cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
635 cfg.dir = 0;
636 cfg.pageAddr = portnum;
637 cfg.timeout = 0;
638
639 if ((rc = mpt_config(ioc, &cfg)) != 0)
640 return rc;
641
642 if (hdr.PageLength == 0)
643 return 0;
644
645 data_sz = hdr.PageLength * 4;
646 rc = -ENOMEM;
647 ppage0_alloc = (FCPortPage0_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page0_dma);
648 if (ppage0_alloc) {
649
650 try_again:
651 memset((u8 *)ppage0_alloc, 0, data_sz);
652 cfg.physAddr = page0_dma;
653 cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
654
655 if ((rc = mpt_config(ioc, &cfg)) == 0) {
656 /* save the data */
657 pp0dest = &ioc->fc_port_page0[portnum];
658 copy_sz = min_t(int, sizeof(FCPortPage0_t), data_sz);
659 memcpy(pp0dest, ppage0_alloc, copy_sz);
660
661 /*
662 * Normalize endianness of structure data,
663 * by byte-swapping all > 1 byte fields!
664 */
665 pp0dest->Flags = le32_to_cpu(pp0dest->Flags);
666 pp0dest->PortIdentifier = le32_to_cpu(pp0dest->PortIdentifier);
667 pp0dest->WWNN.Low = le32_to_cpu(pp0dest->WWNN.Low);
668 pp0dest->WWNN.High = le32_to_cpu(pp0dest->WWNN.High);
669 pp0dest->WWPN.Low = le32_to_cpu(pp0dest->WWPN.Low);
670 pp0dest->WWPN.High = le32_to_cpu(pp0dest->WWPN.High);
671 pp0dest->SupportedServiceClass = le32_to_cpu(pp0dest->SupportedServiceClass);
672 pp0dest->SupportedSpeeds = le32_to_cpu(pp0dest->SupportedSpeeds);
673 pp0dest->CurrentSpeed = le32_to_cpu(pp0dest->CurrentSpeed);
674 pp0dest->MaxFrameSize = le32_to_cpu(pp0dest->MaxFrameSize);
675 pp0dest->FabricWWNN.Low = le32_to_cpu(pp0dest->FabricWWNN.Low);
676 pp0dest->FabricWWNN.High = le32_to_cpu(pp0dest->FabricWWNN.High);
677 pp0dest->FabricWWPN.Low = le32_to_cpu(pp0dest->FabricWWPN.Low);
678 pp0dest->FabricWWPN.High = le32_to_cpu(pp0dest->FabricWWPN.High);
679 pp0dest->DiscoveredPortsCount = le32_to_cpu(pp0dest->DiscoveredPortsCount);
680 pp0dest->MaxInitiators = le32_to_cpu(pp0dest->MaxInitiators);
681
682 /*
683 * if still doing discovery,
684 * hang loose a while until finished
685 */
686 if (pp0dest->PortState == MPI_FCPORTPAGE0_PORTSTATE_UNKNOWN) {
687 if (count-- > 0) {
688 msleep_interruptible(100);
689 goto try_again;
690 }
691 printk(MYIOC_s_INFO_FMT "Firmware discovery not"
692 " complete.\n",
693 ioc->name);
694 }
695 }
696
697 pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage0_alloc, page0_dma);
698 }
699
700 return rc;
701}
702
Michael Reed05e8ec12006-01-13 14:31:54 -0600703static void
704mptfc_init_host_attr(MPT_ADAPTER *ioc,int portnum)
705{
706 unsigned class = 0, cos = 0;
707
708 /* don't know what to do as only one scsi (fc) host was allocated */
709 if (portnum != 0)
710 return;
711
712 class = ioc->fc_port_page0[portnum].SupportedServiceClass;
713 if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_1)
714 cos |= FC_COS_CLASS1;
715 if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_2)
716 cos |= FC_COS_CLASS2;
717 if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_3)
718 cos |= FC_COS_CLASS3;
719
720 fc_host_node_name(ioc->sh) =
721 (u64)ioc->fc_port_page0[portnum].WWNN.High << 32
722 | (u64)ioc->fc_port_page0[portnum].WWNN.Low;
723
724 fc_host_port_name(ioc->sh) =
725 (u64)ioc->fc_port_page0[portnum].WWPN.High << 32
726 | (u64)ioc->fc_port_page0[portnum].WWPN.Low;
727
728 fc_host_port_id(ioc->sh) = ioc->fc_port_page0[portnum].PortIdentifier;
729
730 fc_host_supported_classes(ioc->sh) = cos;
731
732 fc_host_tgtid_bind_type(ioc->sh) = FC_TGTID_BIND_BY_WWPN;
733}
734
735static void
736mptfc_rescan_devices(void *arg)
737{
738 MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg;
739 int ii;
740 int work_to_do;
Moore, Eric65207fe2006-04-21 16:14:35 -0600741 u64 pn;
Michael Reed05e8ec12006-01-13 14:31:54 -0600742 unsigned long flags;
743 struct mptfc_rport_info *ri;
744
745 do {
746 /* start by tagging all ports as missing */
Michael Reed05e8ec12006-01-13 14:31:54 -0600747 list_for_each_entry(ri, &ioc->fc_rports, list) {
748 if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) {
749 ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING;
750 }
751 }
Michael Reed05e8ec12006-01-13 14:31:54 -0600752
753 /*
754 * now rescan devices known to adapter,
755 * will reregister existing rports
756 */
757 for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
Michael Reed80d3ac72006-05-24 15:07:09 -0500758 (void) mptfc_GetFcPortPage0(ioc, ii);
Michael Reed05e8ec12006-01-13 14:31:54 -0600759 mptfc_init_host_attr(ioc,ii); /* refresh */
760 mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev);
761 }
762
763 /* delete devices still missing */
Michael Reed05e8ec12006-01-13 14:31:54 -0600764 list_for_each_entry(ri, &ioc->fc_rports, list) {
765 /* if newly missing, delete it */
Moore, Eric65207fe2006-04-21 16:14:35 -0600766 if (ri->flags & MPT_RPORT_INFO_FLAGS_MISSING) {
Michael Reed05e8ec12006-01-13 14:31:54 -0600767
768 ri->flags &= ~(MPT_RPORT_INFO_FLAGS_REGISTERED|
769 MPT_RPORT_INFO_FLAGS_MISSING);
Moore, Eric65207fe2006-04-21 16:14:35 -0600770 fc_remote_port_delete(ri->rport); /* won't sleep */
Michael Reed3bc7bf12006-01-25 18:05:18 -0700771 ri->rport = NULL;
Moore, Eric65207fe2006-04-21 16:14:35 -0600772
773 pn = (u64)ri->pg0.WWPN.High << 32 |
774 (u64)ri->pg0.WWPN.Low;
Michael Reed3bc7bf12006-01-25 18:05:18 -0700775 dfcprintk ((MYIOC_s_INFO_FMT
776 "mptfc_rescan.%d: %llx deleted\n",
777 ioc->name,
778 ioc->sh->host_no,
Moore, Eric65207fe2006-04-21 16:14:35 -0600779 (unsigned long long)pn));
Michael Reed05e8ec12006-01-13 14:31:54 -0600780 }
781 }
Michael Reed05e8ec12006-01-13 14:31:54 -0600782
783 /*
784 * allow multiple passes as target state
785 * might have changed during scan
786 */
787 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
788 if (ioc->fc_rescan_work_count > 2) /* only need one more */
789 ioc->fc_rescan_work_count = 2;
790 work_to_do = --ioc->fc_rescan_work_count;
791 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
792 } while (work_to_do);
793}
794
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400795static int
796mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
797{
798 struct Scsi_Host *sh;
799 MPT_SCSI_HOST *hd;
800 MPT_ADAPTER *ioc;
801 unsigned long flags;
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +0100802 int ii;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400803 int numSGE = 0;
804 int scale;
805 int ioc_cap;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400806 int error=0;
807 int r;
Michael Reed05e8ec12006-01-13 14:31:54 -0600808
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400809 if ((r = mpt_attach(pdev,id)) != 0)
810 return r;
Michael Reed05e8ec12006-01-13 14:31:54 -0600811
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400812 ioc = pci_get_drvdata(pdev);
Moore, Eric Dean d335cc32005-04-30 17:09:38 -0500813 ioc->DoneCtx = mptfcDoneCtx;
814 ioc->TaskCtx = mptfcTaskCtx;
815 ioc->InternalCtx = mptfcInternalCtx;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400816
817 /* Added sanity check on readiness of the MPT adapter.
818 */
819 if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
820 printk(MYIOC_s_WARN_FMT
821 "Skipping because it's not operational!\n",
822 ioc->name);
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -0700823 error = -ENODEV;
824 goto out_mptfc_probe;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400825 }
826
827 if (!ioc->active) {
828 printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
829 ioc->name);
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -0700830 error = -ENODEV;
831 goto out_mptfc_probe;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400832 }
833
834 /* Sanity check - ensure at least 1 port is INITIATOR capable
835 */
836 ioc_cap = 0;
837 for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
838 if (ioc->pfacts[ii].ProtocolFlags &
839 MPI_PORTFACTS_PROTOCOL_INITIATOR)
840 ioc_cap ++;
841 }
842
843 if (!ioc_cap) {
844 printk(MYIOC_s_WARN_FMT
845 "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n",
846 ioc->name, ioc);
Michael Reed05e8ec12006-01-13 14:31:54 -0600847 return -ENODEV;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400848 }
849
850 sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST));
851
852 if (!sh) {
853 printk(MYIOC_s_WARN_FMT
854 "Unable to register controller with SCSI subsystem\n",
855 ioc->name);
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -0700856 error = -1;
857 goto out_mptfc_probe;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400858 }
859
Michael Reed80d3ac72006-05-24 15:07:09 -0500860 spin_lock_init(&ioc->fc_rescan_work_lock);
Michael Reed05e8ec12006-01-13 14:31:54 -0600861 INIT_WORK(&ioc->fc_rescan_work, mptfc_rescan_devices,(void *)ioc);
862
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400863 spin_lock_irqsave(&ioc->FreeQlock, flags);
864
865 /* Attach the SCSI Host to the IOC structure
866 */
867 ioc->sh = sh;
868
869 sh->io_port = 0;
870 sh->n_io_port = 0;
871 sh->irq = 0;
872
873 /* set 16 byte cdb's */
874 sh->max_cmd_len = 16;
875
876 sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255;
877
878 sh->max_lun = MPT_LAST_LUN + 1;
879 sh->max_channel = 0;
880 sh->this_id = ioc->pfacts[0].PortSCSIID;
881
882 /* Required entry.
883 */
884 sh->unique_id = ioc->id;
885
886 /* Verify that we won't exceed the maximum
887 * number of chain buffers
888 * We can optimize: ZZ = req_sz/sizeof(SGE)
889 * For 32bit SGE's:
890 * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
891 * + (req_sz - 64)/sizeof(SGE)
892 * A slightly different algorithm is required for
893 * 64bit SGEs.
894 */
895 scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
896 if (sizeof(dma_addr_t) == sizeof(u64)) {
897 numSGE = (scale - 1) *
898 (ioc->facts.MaxChainDepth-1) + scale +
899 (ioc->req_sz - 60) / (sizeof(dma_addr_t) +
900 sizeof(u32));
901 } else {
902 numSGE = 1 + (scale - 1) *
903 (ioc->facts.MaxChainDepth-1) + scale +
904 (ioc->req_sz - 64) / (sizeof(dma_addr_t) +
905 sizeof(u32));
906 }
907
908 if (numSGE < sh->sg_tablesize) {
909 /* Reset this value */
910 dprintk((MYIOC_s_INFO_FMT
911 "Resetting sg_tablesize to %d from %d\n",
912 ioc->name, numSGE, sh->sg_tablesize));
913 sh->sg_tablesize = numSGE;
914 }
915
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400916 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
917
918 hd = (MPT_SCSI_HOST *) sh->hostdata;
919 hd->ioc = ioc;
920
921 /* SCSI needs scsi_cmnd lookup table!
922 * (with size equal to req_depth*PtrSz!)
923 */
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +0100924 hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
925 if (!hd->ScsiLookup) {
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400926 error = -ENOMEM;
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -0700927 goto out_mptfc_probe;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400928 }
929
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +0100930 dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p\n",
931 ioc->name, hd->ScsiLookup));
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400932
933 /* Allocate memory for the device structures.
934 * A non-Null pointer at an offset
935 * indicates a device exists.
936 * max_id = 1 + maximum id (hosts.h)
937 */
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +0100938 hd->Targets = kcalloc(sh->max_id, sizeof(void *), GFP_ATOMIC);
939 if (!hd->Targets) {
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400940 error = -ENOMEM;
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -0700941 goto out_mptfc_probe;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400942 }
943
Christoph Hellwig1ca00bb2006-01-13 18:27:50 +0100944 dprintk((KERN_INFO " vdev @ %p\n", hd->Targets));
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400945
946 /* Clear the TM flags
947 */
948 hd->tmPending = 0;
949 hd->tmState = TM_STATE_NONE;
950 hd->resetPending = 0;
951 hd->abortSCpnt = NULL;
952
953 /* Clear the pointer used to store
954 * single-threaded commands, i.e., those
955 * issued during a bus scan, dv and
956 * configuration pages.
957 */
958 hd->cmdPtr = NULL;
959
960 /* Initialize this SCSI Hosts' timers
961 * To use, set the timer expires field
962 * and add_timer
963 */
964 init_timer(&hd->timer);
965 hd->timer.data = (unsigned long) hd;
966 hd->timer.function = mptscsih_timer_expired;
967
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400968 hd->mpt_pq_filter = mpt_pq_filter;
969
970 ddvprintk((MYIOC_s_INFO_FMT
971 "mpt_pq_filter %x\n",
972 ioc->name,
973 mpt_pq_filter));
974
975 init_waitqueue_head(&hd->scandv_waitq);
976 hd->scandv_wait_done = 0;
977 hd->last_queue_full = 0;
978
Michael Reed05e8ec12006-01-13 14:31:54 -0600979 sh->transportt = mptfc_transport_template;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400980 error = scsi_add_host (sh, &ioc->pcidev->dev);
981 if(error) {
982 dprintk((KERN_ERR MYNAM
983 "scsi_add_host failed\n"));
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -0700984 goto out_mptfc_probe;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -0400985 }
986
Moore, Eric65207fe2006-04-21 16:14:35 -0600987 /* initialize workqueue */
988
989 snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d",
990 sh->host_no);
991 ioc->fc_rescan_work_q =
992 create_singlethread_workqueue(ioc->fc_rescan_work_q_name);
993 if (!ioc->fc_rescan_work_q)
994 goto out_mptfc_probe;
995
996 /*
Michael Reed80d3ac72006-05-24 15:07:09 -0500997 * Pre-fetch FC port WWN and stuff...
998 * (FCPortPage0_t stuff)
999 */
1000 for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
1001 (void) mptfc_GetFcPortPage0(ioc, ii);
1002 }
1003
1004 /*
Moore, Eric65207fe2006-04-21 16:14:35 -06001005 * scan for rports -
1006 * by doing it via the workqueue, some locking is eliminated
1007 */
1008
1009 ioc->fc_rescan_work_count = 1;
1010 queue_work(ioc->fc_rescan_work_q, &ioc->fc_rescan_work);
1011 flush_workqueue(ioc->fc_rescan_work_q);
Michael Reed05e8ec12006-01-13 14:31:54 -06001012
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001013 return 0;
1014
Moore, Eric Dean7acec1e2005-11-16 18:54:17 -07001015out_mptfc_probe:
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001016
1017 mptscsih_remove(pdev);
1018 return error;
1019}
1020
1021static struct pci_driver mptfc_driver = {
1022 .name = "mptfc",
1023 .id_table = mptfc_pci_table,
1024 .probe = mptfc_probe,
Michael Reed05e8ec12006-01-13 14:31:54 -06001025 .remove = __devexit_p(mptfc_remove),
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001026 .shutdown = mptscsih_shutdown,
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001027#ifdef CONFIG_PM
1028 .suspend = mptscsih_suspend,
1029 .resume = mptscsih_resume,
1030#endif
1031};
1032
Michael Reed80d3ac72006-05-24 15:07:09 -05001033static int
1034mptfc_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
1035{
1036 MPT_SCSI_HOST *hd;
1037 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
1038 unsigned long flags;
1039 int rc=1;
1040
1041 devtverboseprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
1042 ioc->name, event));
1043
1044 if (ioc->sh == NULL ||
1045 ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL))
1046 return 1;
1047
1048 switch (event) {
1049 case MPI_EVENT_RESCAN:
1050 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
1051 if (ioc->fc_rescan_work_q) {
1052 if (ioc->fc_rescan_work_count++ == 0) {
1053 queue_work(ioc->fc_rescan_work_q,
1054 &ioc->fc_rescan_work);
1055 }
1056 }
1057 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
1058 break;
1059 default:
1060 rc = mptscsih_event_process(ioc,pEvReply);
1061 break;
1062 }
1063 return rc;
1064}
1065
1066static int
1067mptfc_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
1068{
1069 int rc;
1070 unsigned long flags;
1071
1072 rc = mptscsih_ioc_reset(ioc,reset_phase);
1073 if (rc == 0)
1074 return rc;
1075
1076
1077 dtmprintk((KERN_WARNING MYNAM
1078 ": IOC %s_reset routed to FC host driver!\n",
1079 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
1080 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
1081
1082 if (reset_phase == MPT_IOC_SETUP_RESET) {
1083 }
1084
1085 else if (reset_phase == MPT_IOC_PRE_RESET) {
1086 }
1087
1088 else { /* MPT_IOC_POST_RESET */
1089 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
1090 if (ioc->fc_rescan_work_q) {
1091 if (ioc->fc_rescan_work_count++ == 0) {
1092 queue_work(ioc->fc_rescan_work_q,
1093 &ioc->fc_rescan_work);
1094 }
1095 }
1096 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
1097 }
1098 return 1;
1099}
1100
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001101/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1102/**
1103 * mptfc_init - Register MPT adapter(s) as SCSI host(s) with
1104 * linux scsi mid-layer.
1105 *
1106 * Returns 0 for success, non-zero for failure.
1107 */
1108static int __init
1109mptfc_init(void)
1110{
Michael Reed05e8ec12006-01-13 14:31:54 -06001111 int error;
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001112
1113 show_mptmod_ver(my_NAME, my_VERSION);
1114
Michael Reed05e8ec12006-01-13 14:31:54 -06001115 /* sanity check module parameter */
1116 if (mptfc_dev_loss_tmo == 0)
1117 mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO;
1118
1119 mptfc_transport_template =
1120 fc_attach_transport(&mptfc_transport_functions);
1121
1122 if (!mptfc_transport_template)
1123 return -ENODEV;
1124
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001125 mptfcDoneCtx = mpt_register(mptscsih_io_done, MPTFC_DRIVER);
1126 mptfcTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTFC_DRIVER);
1127 mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER);
1128
Michael Reed80d3ac72006-05-24 15:07:09 -05001129 if (mpt_event_register(mptfcDoneCtx, mptfc_event_process) == 0) {
Moore, Eric3a892be2006-03-14 09:14:03 -07001130 devtverboseprintk((KERN_INFO MYNAM
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001131 ": Registered for IOC event notifications\n"));
1132 }
1133
Michael Reed80d3ac72006-05-24 15:07:09 -05001134 if (mpt_reset_register(mptfcDoneCtx, mptfc_ioc_reset) == 0) {
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001135 dprintk((KERN_INFO MYNAM
1136 ": Registered for IOC reset notifications\n"));
1137 }
1138
Michael Reed05e8ec12006-01-13 14:31:54 -06001139 error = pci_register_driver(&mptfc_driver);
Michael Reed3bc7bf12006-01-25 18:05:18 -07001140 if (error)
Michael Reed05e8ec12006-01-13 14:31:54 -06001141 fc_release_transport(mptfc_transport_template);
Michael Reed05e8ec12006-01-13 14:31:54 -06001142
1143 return error;
1144}
1145
1146/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1147/**
1148 * mptfc_remove - Removed fc infrastructure for devices
1149 * @pdev: Pointer to pci_dev structure
1150 *
1151 */
Michael Reed3bc7bf12006-01-25 18:05:18 -07001152static void __devexit
1153mptfc_remove(struct pci_dev *pdev)
Michael Reed05e8ec12006-01-13 14:31:54 -06001154{
Moore, Eric65207fe2006-04-21 16:14:35 -06001155 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
1156 struct mptfc_rport_info *p, *n;
1157 struct workqueue_struct *work_q;
1158 unsigned long flags;
1159
1160 /* destroy workqueue */
1161 if ((work_q=ioc->fc_rescan_work_q)) {
1162 spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags);
1163 ioc->fc_rescan_work_q = NULL;
1164 spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags);
1165 destroy_workqueue(work_q);
1166 }
Michael Reed05e8ec12006-01-13 14:31:54 -06001167
1168 fc_remove_host(ioc->sh);
1169
1170 list_for_each_entry_safe(p, n, &ioc->fc_rports, list) {
1171 list_del(&p->list);
1172 kfree(p);
1173 }
1174
1175 mptscsih_remove(pdev);
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001176}
1177
1178/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1179/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1180/**
1181 * mptfc_exit - Unregisters MPT adapter(s)
1182 *
1183 */
1184static void __exit
1185mptfc_exit(void)
1186{
1187 pci_unregister_driver(&mptfc_driver);
Michael Reed05e8ec12006-01-13 14:31:54 -06001188 fc_release_transport(mptfc_transport_template);
1189
Moore, Eric Dean 2496af32005-04-22 18:02:41 -04001190 mpt_reset_deregister(mptfcDoneCtx);
1191 dprintk((KERN_INFO MYNAM
1192 ": Deregistered for IOC reset notifications\n"));
1193
1194 mpt_event_deregister(mptfcDoneCtx);
1195 dprintk((KERN_INFO MYNAM
1196 ": Deregistered for IOC event notifications\n"));
1197
1198 mpt_deregister(mptfcInternalCtx);
1199 mpt_deregister(mptfcTaskCtx);
1200 mpt_deregister(mptfcDoneCtx);
1201}
1202
1203module_init(mptfc_init);
1204module_exit(mptfc_exit);