aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/scsi/bfa/bfad.c
blob: 6bff08ea40291d6bb84b10c493e9eff806a1d6b7 (plain) (tree)
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666





















                                                                          
                          
































                                                                  
                                       




















                                                           
                                                  




















                                                       

                                                










                                           
                                                 


































































                                                                              
                                           

                                                       









                                                                











                                                                

                                                                         









































































































































































































































































































































































































































































                                                                                
                                               











                                                         


















                                                                            
                                                          




























                                                                               
                                                




























                                                                   
                                                                    






                                                       
                                                               
                                                            














                                                        







                                                        



                                       
                                   

                                                

























































































                                                                          















































































                                                                            











                                                                     





























                                                                         
                                                                          




























                                                                  








                                                                         



                                                       
                                                       



                                                                                
                                      
                                    
                                           
 

                                     
 


                 
                      

                              

                                     























                                                     




                                                        















                                                                
                                                     
                                    










                                                                

                               
                                       


                                                   




















































































































































                                                                           
/*
 * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
 * All rights reserved
 * www.brocade.com
 *
 * Linux driver for Brocade Fibre Channel Host Bus Adapter.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License (GPL) Version 2 as
 * published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

/**
 *  bfad.c Linux driver PCI interface module.
 */

#include <linux/module.h>
#include <linux/kthread.h>
#include "bfad_drv.h"
#include "bfad_im.h"
#include "bfad_tm.h"
#include "bfad_ipfc.h"
#include "bfad_trcmod.h"
#include <fcb/bfa_fcb_vf.h>
#include <fcb/bfa_fcb_rport.h>
#include <fcb/bfa_fcb_port.h>
#include <fcb/bfa_fcb.h>

BFA_TRC_FILE(LDRV, BFAD);
static DEFINE_MUTEX(bfad_mutex);
LIST_HEAD(bfad_list);
static int      bfad_inst;
int bfad_supported_fc4s;

static char     *host_name;
static char     *os_name;
static char     *os_patch;
static int      num_rports;
static int      num_ios;
static int      num_tms;
static int      num_fcxps;
static int      num_ufbufs;
static int      reqq_size;
static int      rspq_size;
static int      num_sgpgs;
static int      rport_del_timeout = BFA_FCS_RPORT_DEF_DEL_TIMEOUT;
static int      bfa_io_max_sge = BFAD_IO_MAX_SGE;
static int      log_level = BFA_LOG_WARNING;
static int      ioc_auto_recover = BFA_TRUE;
static int      ipfc_enable = BFA_FALSE;
static int      ipfc_mtu = -1;
static int	fdmi_enable = BFA_TRUE;
int 		bfa_lun_queue_depth = BFAD_LUN_QUEUE_DEPTH;
int      	bfa_linkup_delay = -1;

module_param(os_name, charp, S_IRUGO | S_IWUSR);
module_param(os_patch, charp, S_IRUGO | S_IWUSR);
module_param(host_name, charp, S_IRUGO | S_IWUSR);
module_param(num_rports, int, S_IRUGO | S_IWUSR);
module_param(num_ios, int, S_IRUGO | S_IWUSR);
module_param(num_tms, int, S_IRUGO | S_IWUSR);
module_param(num_fcxps, int, S_IRUGO | S_IWUSR);
module_param(num_ufbufs, int, S_IRUGO | S_IWUSR);
module_param(reqq_size, int, S_IRUGO | S_IWUSR);
module_param(rspq_size, int, S_IRUGO | S_IWUSR);
module_param(num_sgpgs, int, S_IRUGO | S_IWUSR);
module_param(rport_del_timeout, int, S_IRUGO | S_IWUSR);
module_param(bfa_lun_queue_depth, int, S_IRUGO | S_IWUSR);
module_param(bfa_io_max_sge, int, S_IRUGO | S_IWUSR);
module_param(log_level, int, S_IRUGO | S_IWUSR);
module_param(ioc_auto_recover, int, S_IRUGO | S_IWUSR);
module_param(ipfc_enable, int, S_IRUGO | S_IWUSR);
module_param(ipfc_mtu, int, S_IRUGO | S_IWUSR);
module_param(fdmi_enable, int, S_IRUGO | S_IWUSR);
module_param(bfa_linkup_delay, int, S_IRUGO | S_IWUSR);

/*
 * Stores the module parm num_sgpgs value;
 * used to reset for bfad next instance.
 */
static int num_sgpgs_parm;

static bfa_status_t
bfad_fc4_probe(struct bfad_s *bfad)
{
	int             rc;

	rc = bfad_im_probe(bfad);
	if (rc != BFA_STATUS_OK)
		goto ext;

	bfad_tm_probe(bfad);

	if (ipfc_enable)
		bfad_ipfc_probe(bfad);

	bfad->bfad_flags |= BFAD_FC4_PROBE_DONE;
ext:
	return rc;
}

static void
bfad_fc4_probe_undo(struct bfad_s *bfad)
{
	bfad_im_probe_undo(bfad);
	bfad_tm_probe_undo(bfad);
	if (ipfc_enable)
		bfad_ipfc_probe_undo(bfad);
	bfad->bfad_flags &= ~BFAD_FC4_PROBE_DONE;
}

static void
bfad_fc4_probe_post(struct bfad_s *bfad)
{
	if (bfad->im)
		bfad_im_probe_post(bfad->im);

	bfad_tm_probe_post(bfad);
	if (ipfc_enable)
		bfad_ipfc_probe_post(bfad);
}

static bfa_status_t
bfad_fc4_port_new(struct bfad_s *bfad, struct bfad_port_s *port, int roles)
{
	int             rc = BFA_STATUS_FAILED;

	if (roles & BFA_PORT_ROLE_FCP_IM)
		rc = bfad_im_port_new(bfad, port);
	if (rc != BFA_STATUS_OK)
		goto ext;

	if (roles & BFA_PORT_ROLE_FCP_TM)
		rc = bfad_tm_port_new(bfad, port);
	if (rc != BFA_STATUS_OK)
		goto ext;

	if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
		rc = bfad_ipfc_port_new(bfad, port, port->pvb_type);
ext:
	return rc;
}

static void
bfad_fc4_port_delete(struct bfad_s *bfad, struct bfad_port_s *port, int roles)
{
	if (roles & BFA_PORT_ROLE_FCP_IM)
		bfad_im_port_delete(bfad, port);

	if (roles & BFA_PORT_ROLE_FCP_TM)
		bfad_tm_port_delete(bfad, port);

	if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
		bfad_ipfc_port_delete(bfad, port);
}

/**
 *  BFA callbacks
 */
void
bfad_hcb_comp(void *arg, bfa_status_t status)
{
	struct bfad_hal_comp *fcomp = (struct bfad_hal_comp *)arg;

	fcomp->status = status;
	complete(&fcomp->comp);
}

/**
 * bfa_init callback
 */
void
bfa_cb_init(void *drv, bfa_status_t init_status)
{
	struct bfad_s  *bfad = drv;

	if (init_status == BFA_STATUS_OK) {
		bfad->bfad_flags |= BFAD_HAL_INIT_DONE;

		/* If BFAD_HAL_INIT_FAIL flag is set:
		 * Wake up the kernel thread to start
		 * the bfad operations after HAL init done
		 */
		if ((bfad->bfad_flags & BFAD_HAL_INIT_FAIL)) {
			bfad->bfad_flags &= ~BFAD_HAL_INIT_FAIL;
			wake_up_process(bfad->bfad_tsk);
		}
	}

	complete(&bfad->comp);
}



/**
 *  BFA_FCS callbacks
 */
static struct bfad_port_s *
bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv,
		  struct bfad_vport_s *vp_drv)
{
	return (vp_drv) ? (&(vp_drv)->drv_port)
		: ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport));
}

struct bfad_port_s *
bfa_fcb_port_new(struct bfad_s *bfad, struct bfa_fcs_port_s *port,
		 enum bfa_port_role roles, struct bfad_vf_s *vf_drv,
		 struct bfad_vport_s *vp_drv)
{
	bfa_status_t    rc;
	struct bfad_port_s *port_drv;

	if (!vp_drv && !vf_drv) {
		port_drv = &bfad->pport;
		port_drv->pvb_type = BFAD_PORT_PHYS_BASE;
	} else if (!vp_drv && vf_drv) {
		port_drv = &vf_drv->base_port;
		port_drv->pvb_type = BFAD_PORT_VF_BASE;
	} else if (vp_drv && !vf_drv) {
		port_drv = &vp_drv->drv_port;
		port_drv->pvb_type = BFAD_PORT_PHYS_VPORT;
	} else {
		port_drv = &vp_drv->drv_port;
		port_drv->pvb_type = BFAD_PORT_VF_VPORT;
	}

	port_drv->fcs_port = port;
	port_drv->roles = roles;
	rc = bfad_fc4_port_new(bfad, port_drv, roles);
	if (rc != BFA_STATUS_OK) {
		bfad_fc4_port_delete(bfad, port_drv, roles);
		port_drv = NULL;
	}

	return port_drv;
}

void
bfa_fcb_port_delete(struct bfad_s *bfad, enum bfa_port_role roles,
		    struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
{
	struct bfad_port_s *port_drv;

	/*
	 * this will be only called from rmmod context
	 */
	if (vp_drv && !vp_drv->comp_del) {
		port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv);
		bfa_trc(bfad, roles);
		bfad_fc4_port_delete(bfad, port_drv, roles);
	}
}

void
bfa_fcb_port_online(struct bfad_s *bfad, enum bfa_port_role roles,
		    struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
{
	struct bfad_port_s *port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv);

	if (roles & BFA_PORT_ROLE_FCP_IM)
		bfad_im_port_online(bfad, port_drv);

	if (roles & BFA_PORT_ROLE_FCP_TM)
		bfad_tm_port_online(bfad, port_drv);

	if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
		bfad_ipfc_port_online(bfad, port_drv);

	bfad->bfad_flags |= BFAD_PORT_ONLINE;
}

void
bfa_fcb_port_offline(struct bfad_s *bfad, enum bfa_port_role roles,
		     struct bfad_vf_s *vf_drv, struct bfad_vport_s *vp_drv)
{
	struct bfad_port_s *port_drv = bfad_get_drv_port(bfad, vf_drv, vp_drv);

	if (roles & BFA_PORT_ROLE_FCP_IM)
		bfad_im_port_offline(bfad, port_drv);

	if (roles & BFA_PORT_ROLE_FCP_TM)
		bfad_tm_port_offline(bfad, port_drv);

	if ((roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable)
		bfad_ipfc_port_offline(bfad, port_drv);
}

void
bfa_fcb_vport_delete(struct bfad_vport_s *vport_drv)
{
	if (vport_drv->comp_del) {
		complete(vport_drv->comp_del);
		return;
	}

	kfree(vport_drv);
}

/**
 * FCS RPORT alloc callback, after successful PLOGI by FCS
 */
bfa_status_t
bfa_fcb_rport_alloc(struct bfad_s *bfad, struct bfa_fcs_rport_s **rport,
		    struct bfad_rport_s **rport_drv)
{
	bfa_status_t    rc = BFA_STATUS_OK;

	*rport_drv = kzalloc(sizeof(struct bfad_rport_s), GFP_ATOMIC);
	if (*rport_drv == NULL) {
		rc = BFA_STATUS_ENOMEM;
		goto ext;
	}

	*rport = &(*rport_drv)->fcs_rport;

ext:
	return rc;
}



void
bfad_hal_mem_release(struct bfad_s *bfad)
{
	int             i;
	struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
	struct bfa_mem_elem_s *meminfo_elem;

	for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
		meminfo_elem = &hal_meminfo->meminfo[i];
		if (meminfo_elem->kva != NULL) {
			switch (meminfo_elem->mem_type) {
			case BFA_MEM_TYPE_KVA:
				vfree(meminfo_elem->kva);
				break;
			case BFA_MEM_TYPE_DMA:
				dma_free_coherent(&bfad->pcidev->dev,
						meminfo_elem->mem_len,
						meminfo_elem->kva,
						(dma_addr_t) meminfo_elem->dma);
				break;
			default:
				bfa_assert(0);
				break;
			}
		}
	}

	memset(hal_meminfo, 0, sizeof(struct bfa_meminfo_s));
}

void
bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg)
{
	if (num_rports > 0)
		bfa_cfg->fwcfg.num_rports = num_rports;
	if (num_ios > 0)
		bfa_cfg->fwcfg.num_ioim_reqs = num_ios;
	if (num_tms > 0)
		bfa_cfg->fwcfg.num_tskim_reqs = num_tms;
	if (num_fcxps > 0)
		bfa_cfg->fwcfg.num_fcxp_reqs = num_fcxps;
	if (num_ufbufs > 0)
		bfa_cfg->fwcfg.num_uf_bufs = num_ufbufs;
	if (reqq_size > 0)
		bfa_cfg->drvcfg.num_reqq_elems = reqq_size;
	if (rspq_size > 0)
		bfa_cfg->drvcfg.num_rspq_elems = rspq_size;
	if (num_sgpgs > 0)
		bfa_cfg->drvcfg.num_sgpgs = num_sgpgs;

	/*
	 * populate the hal values back to the driver for sysfs use.
	 * otherwise, the default values will be shown as 0 in sysfs
	 */
	num_rports = bfa_cfg->fwcfg.num_rports;
	num_ios    = bfa_cfg->fwcfg.num_ioim_reqs;
	num_tms	   = bfa_cfg->fwcfg.num_tskim_reqs;
	num_fcxps  = bfa_cfg->fwcfg.num_fcxp_reqs;
	num_ufbufs = bfa_cfg->fwcfg.num_uf_bufs;
	reqq_size  = bfa_cfg->drvcfg.num_reqq_elems;
	rspq_size  = bfa_cfg->drvcfg.num_rspq_elems;
	num_sgpgs  = bfa_cfg->drvcfg.num_sgpgs;
}

bfa_status_t
bfad_hal_mem_alloc(struct bfad_s *bfad)
{
	struct bfa_meminfo_s *hal_meminfo = &bfad->meminfo;
	struct bfa_mem_elem_s *meminfo_elem;
	bfa_status_t    rc = BFA_STATUS_OK;
	dma_addr_t      phys_addr;
	int             retry_count = 0;
	int             reset_value = 1;
	int             min_num_sgpgs = 512;
	void           *kva;
	int             i;

	bfa_cfg_get_default(&bfad->ioc_cfg);

retry:
	bfad_update_hal_cfg(&bfad->ioc_cfg);
	bfad->cfg_data.ioc_queue_depth = bfad->ioc_cfg.fwcfg.num_ioim_reqs;
	bfa_cfg_get_meminfo(&bfad->ioc_cfg, hal_meminfo);

	for (i = 0; i < BFA_MEM_TYPE_MAX; i++) {
		meminfo_elem = &hal_meminfo->meminfo[i];
		switch (meminfo_elem->mem_type) {
		case BFA_MEM_TYPE_KVA:
			kva = vmalloc(meminfo_elem->mem_len);
			if (kva == NULL) {
				bfad_hal_mem_release(bfad);
				rc = BFA_STATUS_ENOMEM;
				goto ext;
			}
			memset(kva, 0, meminfo_elem->mem_len);
			meminfo_elem->kva = kva;
			break;
		case BFA_MEM_TYPE_DMA:
			kva = dma_alloc_coherent(&bfad->pcidev->dev,
					meminfo_elem->mem_len,
					&phys_addr, GFP_KERNEL);
			if (kva == NULL) {
				bfad_hal_mem_release(bfad);
				/*
				 * If we cannot allocate with default
				 * num_sgpages try with half the value.
				 */
				if (num_sgpgs > min_num_sgpgs) {
					printk(KERN_INFO "bfad[%d]: memory"
						" allocation failed with"
						" num_sgpgs: %d\n",
						bfad->inst_no, num_sgpgs);
					nextLowerInt(&num_sgpgs);
					printk(KERN_INFO "bfad[%d]: trying to"
						" allocate memory with"
						" num_sgpgs: %d\n",
						bfad->inst_no, num_sgpgs);
					retry_count++;
					goto retry;
				} else {
					if (num_sgpgs_parm > 0)
						num_sgpgs = num_sgpgs_parm;
					else {
						reset_value =
							(1 << retry_count);
						num_sgpgs *= reset_value;
					}
					rc = BFA_STATUS_ENOMEM;
					goto ext;
				}
			}

			if (num_sgpgs_parm > 0)
				num_sgpgs = num_sgpgs_parm;
			else {
				reset_value = (1 << retry_count);
				num_sgpgs *= reset_value;
			}

			memset(kva, 0, meminfo_elem->mem_len);
			meminfo_elem->kva = kva;
			meminfo_elem->dma = phys_addr;
			break;
		default:
			break;

		}
	}
ext:
	return rc;
}

/**
 * Create a vport under a vf.
 */
bfa_status_t
bfad_vport_create(struct bfad_s *bfad, u16 vf_id,
		  struct bfa_port_cfg_s *port_cfg)
{
	struct bfad_vport_s *vport;
	int             rc = BFA_STATUS_OK;
	unsigned long   flags;
	struct completion fcomp;

	vport = kzalloc(sizeof(struct bfad_vport_s), GFP_KERNEL);
	if (!vport) {
		rc = BFA_STATUS_ENOMEM;
		goto ext;
	}

	vport->drv_port.bfad = bfad;
	spin_lock_irqsave(&bfad->bfad_lock, flags);
	rc = bfa_fcs_vport_create(&vport->fcs_vport, &bfad->bfa_fcs, vf_id,
				  port_cfg, vport);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	if (rc != BFA_STATUS_OK)
		goto ext_free_vport;

	if (port_cfg->roles & BFA_PORT_ROLE_FCP_IM) {
		rc = bfad_im_scsi_host_alloc(bfad, vport->drv_port.im_port);
		if (rc != BFA_STATUS_OK)
			goto ext_free_fcs_vport;
	}

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	bfa_fcs_vport_start(&vport->fcs_vport);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	return BFA_STATUS_OK;

ext_free_fcs_vport:
	spin_lock_irqsave(&bfad->bfad_lock, flags);
	vport->comp_del = &fcomp;
	init_completion(vport->comp_del);
	bfa_fcs_vport_delete(&vport->fcs_vport);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
	wait_for_completion(vport->comp_del);
ext_free_vport:
	kfree(vport);
ext:
	return rc;
}

/**
 * Create a vf and its base vport implicitely.
 */
bfa_status_t
bfad_vf_create(struct bfad_s *bfad, u16 vf_id,
	       struct bfa_port_cfg_s *port_cfg)
{
	struct bfad_vf_s *vf;
	int             rc = BFA_STATUS_OK;

	vf = kzalloc(sizeof(struct bfad_vf_s), GFP_KERNEL);
	if (!vf) {
		rc = BFA_STATUS_FAILED;
		goto ext;
	}

	rc = bfa_fcs_vf_create(&vf->fcs_vf, &bfad->bfa_fcs, vf_id, port_cfg,
			       vf);
	if (rc != BFA_STATUS_OK)
		kfree(vf);
ext:
	return rc;
}

void
bfad_bfa_tmo(unsigned long data)
{
	struct bfad_s  *bfad = (struct bfad_s *)data;
	unsigned long   flags;
	struct list_head  doneq;

	spin_lock_irqsave(&bfad->bfad_lock, flags);

	bfa_timer_tick(&bfad->bfa);

	bfa_comp_deq(&bfad->bfa, &doneq);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	if (!list_empty(&doneq)) {
		bfa_comp_process(&bfad->bfa, &doneq);
		spin_lock_irqsave(&bfad->bfad_lock, flags);
		bfa_comp_free(&bfad->bfa, &doneq);
		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
	}

	mod_timer(&bfad->hal_tmo, jiffies + msecs_to_jiffies(BFA_TIMER_FREQ));
}

void
bfad_init_timer(struct bfad_s *bfad)
{
	init_timer(&bfad->hal_tmo);
	bfad->hal_tmo.function = bfad_bfa_tmo;
	bfad->hal_tmo.data = (unsigned long)bfad;

	mod_timer(&bfad->hal_tmo, jiffies + msecs_to_jiffies(BFA_TIMER_FREQ));
}

int
bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad)
{
	unsigned long   bar0_len;
	int             rc = -ENODEV;

	if (pci_enable_device(pdev)) {
		BFA_PRINTF(BFA_ERR, "pci_enable_device fail %p\n", pdev);
		goto out;
	}

	if (pci_request_regions(pdev, BFAD_DRIVER_NAME))
		goto out_disable_device;

	pci_set_master(pdev);


	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)
		if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
			BFA_PRINTF(BFA_ERR, "pci_set_dma_mask fail %p\n", pdev);
			goto out_release_region;
		}

	bfad->pci_bar0_map = pci_resource_start(pdev, 0);
	bar0_len = pci_resource_len(pdev, 0);
	bfad->pci_bar0_kva = ioremap(bfad->pci_bar0_map, bar0_len);

	if (bfad->pci_bar0_kva == NULL) {
		BFA_PRINTF(BFA_ERR, "Fail to map bar0\n");
		goto out_release_region;
	}

	bfad->hal_pcidev.pci_slot = PCI_SLOT(pdev->devfn);
	bfad->hal_pcidev.pci_func = PCI_FUNC(pdev->devfn);
	bfad->hal_pcidev.pci_bar_kva = bfad->pci_bar0_kva;
	bfad->hal_pcidev.device_id = pdev->device;
	bfad->pci_name = pci_name(pdev);

	bfad->pci_attr.vendor_id = pdev->vendor;
	bfad->pci_attr.device_id = pdev->device;
	bfad->pci_attr.ssid = pdev->subsystem_device;
	bfad->pci_attr.ssvid = pdev->subsystem_vendor;
	bfad->pci_attr.pcifn = PCI_FUNC(pdev->devfn);

	bfad->pcidev = pdev;
	return 0;

out_release_region:
	pci_release_regions(pdev);
out_disable_device:
	pci_disable_device(pdev);
out:
	return rc;
}

void
bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad)
{
#if defined(__ia64__)
	pci_iounmap(pdev, bfad->pci_bar0_kva);
#else
	iounmap(bfad->pci_bar0_kva);
#endif
	pci_release_regions(pdev);
	pci_disable_device(pdev);
	pci_set_drvdata(pdev, NULL);
}

void
bfad_fcs_port_cfg(struct bfad_s *bfad)
{
	struct bfa_port_cfg_s port_cfg;
	struct bfa_pport_attr_s attr;
	char            symname[BFA_SYMNAME_MAXLEN];

	sprintf(symname, "%s-%d", BFAD_DRIVER_NAME, bfad->inst_no);
	memcpy(port_cfg.sym_name.symname, symname, strlen(symname));
	bfa_fcport_get_attr(&bfad->bfa, &attr);
	port_cfg.nwwn = attr.nwwn;
	port_cfg.pwwn = attr.pwwn;

	bfa_fcs_cfg_base_port(&bfad->bfa_fcs, &port_cfg);
}

bfa_status_t
bfad_drv_init(struct bfad_s *bfad)
{
	bfa_status_t    rc;
	unsigned long   flags;
	struct bfa_fcs_driver_info_s driver_info;

	bfad->cfg_data.rport_del_timeout = rport_del_timeout;
	bfad->cfg_data.lun_queue_depth = bfa_lun_queue_depth;
	bfad->cfg_data.io_max_sge = bfa_io_max_sge;
	bfad->cfg_data.binding_method = FCP_PWWN_BINDING;

	rc = bfad_hal_mem_alloc(bfad);
	if (rc != BFA_STATUS_OK) {
		printk(KERN_WARNING "bfad%d bfad_hal_mem_alloc failure\n",
		       bfad->inst_no);
		printk(KERN_WARNING
			"Not enough memory to attach all Brocade HBA ports,"
			" System may need more memory.\n");
		goto out_hal_mem_alloc_failure;
	}

	bfa_init_log(&bfad->bfa, bfad->logmod);
	bfa_init_trc(&bfad->bfa, bfad->trcmod);
	bfa_init_aen(&bfad->bfa, bfad->aen);
	memset(bfad->file_map, 0, sizeof(bfad->file_map));
	bfa_init_plog(&bfad->bfa, &bfad->plog_buf);
	bfa_plog_init(&bfad->plog_buf);
	bfa_plog_str(&bfad->plog_buf, BFA_PL_MID_DRVR, BFA_PL_EID_DRIVER_START,
		     0, "Driver Attach");

	bfa_attach(&bfad->bfa, bfad, &bfad->ioc_cfg, &bfad->meminfo,
		   &bfad->hal_pcidev);

	init_completion(&bfad->comp);

	/*
	 * Enable Interrupt and wait bfa_init completion
	 */
	if (bfad_setup_intr(bfad)) {
		printk(KERN_WARNING "bfad%d: bfad_setup_intr failed\n",
		       bfad->inst_no);
		goto out_setup_intr_failure;
	}

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	bfa_init(&bfad->bfa);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	/*
	 * Set up interrupt handler for each vectors
	 */
	if ((bfad->bfad_flags & BFAD_MSIX_ON)
	    && bfad_install_msix_handler(bfad)) {
		printk(KERN_WARNING "%s: install_msix failed, bfad%d\n",
		       __func__, bfad->inst_no);
	}

	bfad_init_timer(bfad);

	wait_for_completion(&bfad->comp);

	memset(&driver_info, 0, sizeof(driver_info));
	strncpy(driver_info.version, BFAD_DRIVER_VERSION,
		sizeof(driver_info.version) - 1);
	if (host_name)
		strncpy(driver_info.host_machine_name, host_name,
			sizeof(driver_info.host_machine_name) - 1);
	if (os_name)
		strncpy(driver_info.host_os_name, os_name,
			sizeof(driver_info.host_os_name) - 1);
	if (os_patch)
		strncpy(driver_info.host_os_patch, os_patch,
			sizeof(driver_info.host_os_patch) - 1);

	strncpy(driver_info.os_device_name, bfad->pci_name,
		sizeof(driver_info.os_device_name - 1));

	/*
	 * FCS INIT
	 */
	spin_lock_irqsave(&bfad->bfad_lock, flags);
	bfa_fcs_log_init(&bfad->bfa_fcs, bfad->logmod);
	bfa_fcs_trc_init(&bfad->bfa_fcs, bfad->trcmod);
	bfa_fcs_aen_init(&bfad->bfa_fcs, bfad->aen);
	bfa_fcs_attach(&bfad->bfa_fcs, &bfad->bfa, bfad, BFA_FALSE);

	/* Do FCS init only when HAL init is done */
	if ((bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
		bfa_fcs_init(&bfad->bfa_fcs);
		bfad->bfad_flags |= BFAD_FCS_INIT_DONE;
	}

	bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info);
	bfa_fcs_set_fdmi_param(&bfad->bfa_fcs, fdmi_enable);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	bfad->bfad_flags |= BFAD_DRV_INIT_DONE;
	return BFA_STATUS_OK;

out_setup_intr_failure:
	bfa_detach(&bfad->bfa);
	bfad_hal_mem_release(bfad);
out_hal_mem_alloc_failure:
	return BFA_STATUS_FAILED;
}

void
bfad_drv_uninit(struct bfad_s *bfad)
{
	unsigned long   flags;

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	init_completion(&bfad->comp);
	bfa_stop(&bfad->bfa);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
	wait_for_completion(&bfad->comp);

	del_timer_sync(&bfad->hal_tmo);
	bfa_isr_disable(&bfad->bfa);
	bfa_detach(&bfad->bfa);
	bfad_remove_intr(bfad);
	bfad_hal_mem_release(bfad);

	bfad->bfad_flags &= ~BFAD_DRV_INIT_DONE;
}

void
bfad_drv_start(struct bfad_s *bfad)
{
	unsigned long   flags;

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	bfa_start(&bfad->bfa);
	bfa_fcs_start(&bfad->bfa_fcs);
	bfad->bfad_flags |= BFAD_HAL_START_DONE;
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	bfad_fc4_probe_post(bfad);
}

void
bfad_drv_stop(struct bfad_s *bfad)
{
	unsigned long   flags;

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	init_completion(&bfad->comp);
	bfad->pport.flags |= BFAD_PORT_DELETE;
	bfa_fcs_exit(&bfad->bfa_fcs);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
	wait_for_completion(&bfad->comp);

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	init_completion(&bfad->comp);
	bfa_stop(&bfad->bfa);
	bfad->bfad_flags &= ~BFAD_HAL_START_DONE;
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
	wait_for_completion(&bfad->comp);
}

bfa_status_t
bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role)
{
	int             rc = BFA_STATUS_OK;

	/*
	 * Allocate scsi_host for the physical port
	 */
	if ((bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IM)
	    && (role & BFA_PORT_ROLE_FCP_IM)) {
		if (bfad->pport.im_port == NULL) {
			rc = BFA_STATUS_FAILED;
			goto out;
		}

		rc = bfad_im_scsi_host_alloc(bfad, bfad->pport.im_port);
		if (rc != BFA_STATUS_OK)
			goto out;

		bfad->pport.roles |= BFA_PORT_ROLE_FCP_IM;
	}

	bfad->bfad_flags |= BFAD_CFG_PPORT_DONE;

out:
	return rc;
}

void
bfad_uncfg_pport(struct bfad_s *bfad)
{
	if ((bfad->pport.roles & BFA_PORT_ROLE_FCP_IPFC) && ipfc_enable) {
		bfad_ipfc_port_delete(bfad, &bfad->pport);
		bfad->pport.roles &= ~BFA_PORT_ROLE_FCP_IPFC;
	}

	if ((bfad_supported_fc4s & BFA_PORT_ROLE_FCP_IM)
	    && (bfad->pport.roles & BFA_PORT_ROLE_FCP_IM)) {
		bfad_im_scsi_host_free(bfad, bfad->pport.im_port);
		bfad_im_port_clean(bfad->pport.im_port);
		kfree(bfad->pport.im_port);
		bfad->pport.roles &= ~BFA_PORT_ROLE_FCP_IM;
	}

	bfad->bfad_flags &= ~BFAD_CFG_PPORT_DONE;
}

void
bfad_drv_log_level_set(struct bfad_s *bfad)
{
	if (log_level > BFA_LOG_INVALID && log_level <= BFA_LOG_LEVEL_MAX)
		bfa_log_set_level_all(&bfad->log_data, log_level);
}

bfa_status_t
bfad_start_ops(struct bfad_s *bfad)
{
	int retval;

	/* PPORT FCS config */
	bfad_fcs_port_cfg(bfad);

	retval = bfad_cfg_pport(bfad, BFA_PORT_ROLE_FCP_IM);
	if (retval != BFA_STATUS_OK)
		goto out_cfg_pport_failure;

	/* BFAD level FC4 (IM/TM/IPFC) specific resource allocation */
	retval = bfad_fc4_probe(bfad);
	if (retval != BFA_STATUS_OK) {
		printk(KERN_WARNING "bfad_fc4_probe failed\n");
		goto out_fc4_probe_failure;
	}

	bfad_drv_start(bfad);

	/*
	 * If bfa_linkup_delay is set to -1 default; try to retrive the
	 * value using the bfad_os_get_linkup_delay(); else use the
	 * passed in module param value as the bfa_linkup_delay.
	 */
	if (bfa_linkup_delay < 0) {

		bfa_linkup_delay = bfad_os_get_linkup_delay(bfad);
		bfad_os_rport_online_wait(bfad);
		bfa_linkup_delay = -1;

	} else {
		bfad_os_rport_online_wait(bfad);
	}

	bfa_log(bfad->logmod, BFA_LOG_LINUX_DEVICE_CLAIMED, bfad->pci_name);

	return BFA_STATUS_OK;

out_fc4_probe_failure:
	bfad_fc4_probe_undo(bfad);
	bfad_uncfg_pport(bfad);
out_cfg_pport_failure:
	return BFA_STATUS_FAILED;
}

int
bfad_worker (void *ptr)
{
	struct bfad_s *bfad;
	unsigned long   flags;

	bfad = (struct bfad_s *)ptr;

	while (!kthread_should_stop()) {

		/* Check if the FCS init is done from bfad_drv_init;
		 * if not done do FCS init and set the flag.
		 */
		if (!(bfad->bfad_flags & BFAD_FCS_INIT_DONE)) {
			spin_lock_irqsave(&bfad->bfad_lock, flags);
			bfa_fcs_init(&bfad->bfa_fcs);
			bfad->bfad_flags |= BFAD_FCS_INIT_DONE;
			spin_unlock_irqrestore(&bfad->bfad_lock, flags);
		}

		/* Start the bfad operations after HAL init done */
		bfad_start_ops(bfad);

		spin_lock_irqsave(&bfad->bfad_lock, flags);
		bfad->bfad_tsk = NULL;
		spin_unlock_irqrestore(&bfad->bfad_lock, flags);

		break;
	}

	return 0;
}

 /*
  *  PCI_entry PCI driver entries * {
  */

/**
 * PCI probe entry.
 */
int
bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
{
	struct bfad_s  *bfad;
	int             error = -ENODEV, retval;

	/*
	 * For single port cards - only claim function 0
	 */
	if ((pdev->device == BFA_PCI_DEVICE_ID_FC_8G1P)
	    && (PCI_FUNC(pdev->devfn) != 0))
		return -ENODEV;

	BFA_TRACE(BFA_INFO, "bfad_pci_probe entry");

	bfad = kzalloc(sizeof(struct bfad_s), GFP_KERNEL);
	if (!bfad) {
		error = -ENOMEM;
		goto out;
	}

	bfad->trcmod = kzalloc(sizeof(struct bfa_trc_mod_s), GFP_KERNEL);
	if (!bfad->trcmod) {
		printk(KERN_WARNING "Error alloc trace buffer!\n");
		error = -ENOMEM;
		goto out_alloc_trace_failure;
	}

	/*
	 * LOG/TRACE INIT
	 */
	bfa_trc_init(bfad->trcmod);
	bfa_trc(bfad, bfad_inst);

	bfad->logmod = &bfad->log_data;
	bfa_log_init(bfad->logmod, (char *)pci_name(pdev), bfa_os_printf);

	bfad_drv_log_level_set(bfad);

	bfad->aen = &bfad->aen_buf;

	if (!(bfad_load_fwimg(pdev))) {
		printk(KERN_WARNING "bfad_load_fwimg failure!\n");
		kfree(bfad->trcmod);
		goto out_alloc_trace_failure;
	}

	retval = bfad_pci_init(pdev, bfad);
	if (retval) {
		printk(KERN_WARNING "bfad_pci_init failure!\n");
		error = retval;
		goto out_pci_init_failure;
	}

	mutex_lock(&bfad_mutex);
	bfad->inst_no = bfad_inst++;
	list_add_tail(&bfad->list_entry, &bfad_list);
	mutex_unlock(&bfad_mutex);

	spin_lock_init(&bfad->bfad_lock);
	pci_set_drvdata(pdev, bfad);

	bfad->ref_count = 0;
	bfad->pport.bfad = bfad;

	bfad->bfad_tsk = kthread_create(bfad_worker, (void *) bfad, "%s",
					"bfad_worker");
	if (IS_ERR(bfad->bfad_tsk)) {
		printk(KERN_INFO "bfad[%d]: Kernel thread"
			" creation failed!\n",
			bfad->inst_no);
		goto out_kthread_create_failure;
	}

	retval = bfad_drv_init(bfad);
	if (retval != BFA_STATUS_OK)
		goto out_drv_init_failure;
	if (!(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
		bfad->bfad_flags |= BFAD_HAL_INIT_FAIL;
		printk(KERN_WARNING "bfad%d: hal init failed\n", bfad->inst_no);
		goto ok;
	}

	retval = bfad_start_ops(bfad);
	if (retval != BFA_STATUS_OK)
		goto out_start_ops_failure;

	kthread_stop(bfad->bfad_tsk);
	bfad->bfad_tsk = NULL;

ok:
	return 0;

out_start_ops_failure:
	bfad_drv_uninit(bfad);
out_drv_init_failure:
	kthread_stop(bfad->bfad_tsk);
out_kthread_create_failure:
	mutex_lock(&bfad_mutex);
	bfad_inst--;
	list_del(&bfad->list_entry);
	mutex_unlock(&bfad_mutex);
	bfad_pci_uninit(pdev, bfad);
out_pci_init_failure:
	kfree(bfad->trcmod);
out_alloc_trace_failure:
	kfree(bfad);
out:
	return error;
}

/**
 * PCI remove entry.
 */
void
bfad_pci_remove(struct pci_dev *pdev)
{
	struct bfad_s  *bfad = pci_get_drvdata(pdev);
	unsigned long   flags;

	bfa_trc(bfad, bfad->inst_no);

	spin_lock_irqsave(&bfad->bfad_lock, flags);
	if (bfad->bfad_tsk != NULL)
		kthread_stop(bfad->bfad_tsk);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);

	if ((bfad->bfad_flags & BFAD_DRV_INIT_DONE)
	    && !(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {

		spin_lock_irqsave(&bfad->bfad_lock, flags);
		init_completion(&bfad->comp);
		bfa_stop(&bfad->bfa);
		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
		wait_for_completion(&bfad->comp);

		bfad_remove_intr(bfad);
		del_timer_sync(&bfad->hal_tmo);
		goto hal_detach;
	} else if (!(bfad->bfad_flags & BFAD_DRV_INIT_DONE)) {
		goto remove_sysfs;
	}

	if (bfad->bfad_flags & BFAD_HAL_START_DONE) {
		bfad_drv_stop(bfad);
	} else if (bfad->bfad_flags & BFAD_DRV_INIT_DONE) {
		/* Invoking bfa_stop() before bfa_detach
		 * when HAL and DRV init are success
		 * but HAL start did not occur.
		 */
		spin_lock_irqsave(&bfad->bfad_lock, flags);
		init_completion(&bfad->comp);
		bfa_stop(&bfad->bfa);
		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
		wait_for_completion(&bfad->comp);
	}

	bfad_remove_intr(bfad);
	del_timer_sync(&bfad->hal_tmo);

	if (bfad->bfad_flags & BFAD_FC4_PROBE_DONE)
		bfad_fc4_probe_undo(bfad);

	if (bfad->bfad_flags & BFAD_CFG_PPORT_DONE)
		bfad_uncfg_pport(bfad);

hal_detach:
	spin_lock_irqsave(&bfad->bfad_lock, flags);
	bfa_detach(&bfad->bfa);
	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
	bfad_hal_mem_release(bfad);
remove_sysfs:

	mutex_lock(&bfad_mutex);
	bfad_inst--;
	list_del(&bfad->list_entry);
	mutex_unlock(&bfad_mutex);
	bfad_pci_uninit(pdev, bfad);

	kfree(bfad->trcmod);
	kfree(bfad);
}


static struct pci_device_id bfad_id_table[] = {
	{
	 .vendor = BFA_PCI_VENDOR_ID_BROCADE,
	 .device = BFA_PCI_DEVICE_ID_FC_8G2P,
	 .subvendor = PCI_ANY_ID,
	 .subdevice = PCI_ANY_ID,
	 },
	{
	 .vendor = BFA_PCI_VENDOR_ID_BROCADE,
	 .device = BFA_PCI_DEVICE_ID_FC_8G1P,
	 .subvendor = PCI_ANY_ID,
	 .subdevice = PCI_ANY_ID,
	 },
	{
	 .vendor = BFA_PCI_VENDOR_ID_BROCADE,
	 .device = BFA_PCI_DEVICE_ID_CT,
	 .subvendor = PCI_ANY_ID,
	 .subdevice = PCI_ANY_ID,
	 .class = (PCI_CLASS_SERIAL_FIBER << 8),
	 .class_mask = ~0,
	 },

	{0, 0},
};

MODULE_DEVICE_TABLE(pci, bfad_id_table);

static struct pci_driver bfad_pci_driver = {
	.name = BFAD_DRIVER_NAME,
	.id_table = bfad_id_table,
	.probe = bfad_pci_probe,
	.remove = __devexit_p(bfad_pci_remove),
};

/**
 *  Linux driver module functions
 */
bfa_status_t
bfad_fc4_module_init(void)
{
	int             rc;

	rc = bfad_im_module_init();
	if (rc != BFA_STATUS_OK)
		goto ext;

	bfad_tm_module_init();
	if (ipfc_enable)
		bfad_ipfc_module_init();
ext:
	return rc;
}

void
bfad_fc4_module_exit(void)
{
	if (ipfc_enable)
		bfad_ipfc_module_exit();
	bfad_tm_module_exit();
	bfad_im_module_exit();
}

/**
 * Driver module init.
 */
static int      __init
bfad_init(void)
{
	int             error = 0;

	printk(KERN_INFO "Brocade BFA FC/FCOE SCSI driver - version: %s\n",
	       BFAD_DRIVER_VERSION);

	if (num_sgpgs > 0)
		num_sgpgs_parm = num_sgpgs;

	error = bfad_fc4_module_init();
	if (error) {
		error = -ENOMEM;
		printk(KERN_WARNING "bfad_fc4_module_init failure\n");
		goto ext;
	}

	if (!strcmp(FCPI_NAME, " fcpim"))
		bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_IM;
	if (!strcmp(FCPT_NAME, " fcptm"))
		bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_TM;
	if (!strcmp(IPFC_NAME, " ipfc"))
		bfad_supported_fc4s |= BFA_PORT_ROLE_FCP_IPFC;

	bfa_ioc_auto_recover(ioc_auto_recover);
	bfa_fcs_rport_set_del_timeout(rport_del_timeout);
	error = pci_register_driver(&bfad_pci_driver);

	if (error) {
		printk(KERN_WARNING "bfad pci_register_driver failure\n");
		goto ext;
	}

	return 0;

ext:
	bfad_fc4_module_exit();
	return error;
}

/**
 * Driver module exit.
 */
static void     __exit
bfad_exit(void)
{
	pci_unregister_driver(&bfad_pci_driver);
	bfad_fc4_module_exit();
	bfad_free_fwimg();
}

#define BFAD_PROTO_NAME FCPI_NAME FCPT_NAME IPFC_NAME

module_init(bfad_init);
module_exit(bfad_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Brocade Fibre Channel HBA Driver" BFAD_PROTO_NAME);
MODULE_AUTHOR("Brocade Communications Systems, Inc.");
MODULE_VERSION(BFAD_DRIVER_VERSION);